Index: head/contrib/tcsh/Fixes =================================================================== --- head/contrib/tcsh/Fixes (revision 315951) +++ head/contrib/tcsh/Fixes (revision 315952) @@ -1,2151 +1,2152 @@ + 22. Fix type of read in prompt confirmation (eg. rmstar) (David Kaspar) 20. V6.20.00 - 20161124 19. Don't resize the screen if it did not change size. 18. V6.19.01 - 20161025 17. restore file description when cleaning up after eval: repeat 99 time 16. PR/572: Fix $SHLVL issue when exec'ing subshells. 15. PR/403: Fix backquote expansion for multi-byte character sets. 14. Fix drawing issu with multi-line prompt (Kensuke Iwahashi/David Kaspar) 13. always send prusage to stdout. 12. PR/526: Fix double \\ printing from previous fix in history expansion. 11. Android updates from Corinna Vinschen 10. PR/526: Quote backslashes properly so they can be preserved in `` expansions 9. Fix memory leak for paraml 8. Add notempty and ask values for the noclobber setting (Martin Tournoij) 7. more correct $wordchars for vimode (Luke Mewburn) 6. expose VImode in $vimode (Luke Mewburn) 5. display what the compiled in editor is in bindkey -d (Luke Mewburn) 4. run-fg-editor improvements and documentation (Luke Mewburn) 3. Fix parsing of 'if (cond)then' (Fridolin Pokorny) 2. PR/437: Fix handling of invalid unicode characters. 1. PR/451: Fix error messages containing %c to be always '%c' 41. V6.19.00 - 20150521 40. V6.18.05 - 20150510 39. fix reseting when interrupted inside an eval "eval sleep 10^C" (paulo.cesar.pereira.de.andrade) 38. rename handle_intr -> handle_interrupt as originally intended. 37. fix input tests that need stdin on a tty 36. V6.18.04 - 20150504 35. revert fix echo "\1", it is incorrect. 34. revert fix to PR/437, breaks short strings. 33. V6.18.03 - 20150503 32. PR/437: Nakajima Akira: Fix segmentation fault reading input files 31. PR/291: Print job status messages to stderr. 30. Fridolin Pokorny NUL in `` does not mean EOF. 29. Pavel Raiskup fix hang with: while (1) ( date & ; wait ) end 28. Add cdtohome special variable (Martin Tournoij) 27. Fix root prompt char for windows (Corinna Vinschen) 26. For "next" completion matches only consider exact matches of the previous word (Jamie Landeg-Jones) 25. Fix echo "\1" for echo_style=both where the first character was not processed properly (Gary Duzan) 24. V6.18.02 - 20140618 23. fix ls-F /non printing exit value twice. 22. rename configure.in to configure.ac, add aclocal.m4 to CVS 21. set foo="aabaabaa"; echo $foo:as/a// should produce bb 20. Add locked merge history support (Marcin Konarski) 19. Support more resource limits from various BSD's 18. Cache history count to speed up thing 17. PR/240: minix support 16. revert fix for 15. Causes extra quoting, for example (foo is a program that prints its arguments): $ ./foo 'abc' * '\a\b\c' 15. fix globbing for ``, stripping backslashes. Example: cat << _EOF > huh echo 'hello\;world' _EOF echo `./huh 0` echo `./huh $?` 14. fix for `` that causes hang. Example: cat << _EOF > huh #!/bin/sh echo "[$@]" echo "I am running" echo "I am running ($$)" >> huh.out _EOF cat << _EOF > huh.tcsh #!/bin/tcsh -f ./huh \ `#comment blah blah blah` \ parameter a \ `#comment blah blah blah` \ parameter b \ `#comment blah blah blah` \ parameter c echo ok _EOF 13. remove AsciiOnly fix now that the real issue has been fixed (Roman Kollar) 12. define utmp file for aix (Laurence Darby) 11. fix if history in loops 10. make ls-F print to stderr and set the exit code 9. make rmstar interruptible on linux 8. Get rid of pret_t and make the printf functions return the number of characters printed as the system ones do. 7. Parse a FreeBSD compat $LSCOLORS. What to do when both LSCOLORS and LS_COLORS are set. I am not documenting this until we decide. 6. V6.18.01 - 20120214 5. fix interruptible wait again 4. ignore bogus compiler overflow message 3. cleanup ifdefs in utmp code, and provide default array entries 2. Ignore #machine entries in host.defs 1. Detect missing ) in gethost.c (Corinna Vinschen) 104. V6.18.00 - 20120114 103. remove unused variables. 102. Make gethost use definitions for x __x__ and __x automatically. 101. More utmp fixes 100. V6.17.10 - 20120105 99. Add more FreeBSD/NetBSD machines 98. Add portability wrapper for gencat 97. Fix warning for write in SYSMALLOC systems. 96. V6.17.09 - 20120102 95. revert gencat handling to pre-cygwin fixes (without the env settings) 94. remove stray endutent() 93. V6.17.08 - 20111230 92. Remove - from gencat 91. Provide support for malloc_usable_size() so that linux works again without SYSMALLOC 90. Add support for FreeBSD's utmpx. 89. V6.17.07 - 20111227 88. Fix debian bug #645238: tcsh segfaults when prompt includes %j and there are more than 10 jobs. 87. PR/155: Default $anyerror to set for backward compatibility 86. PR/149: Don't print -1 in %j (Vojtech Vitek) 85. handle -- on chdir commands as the end of options processing so that they can process a directory like -x without resorting to ./-x (Andrew Stevenson) 84. Handle write(2) returning ENOENT from SoFS, thanks ++HAL (Robert Byrnes) 83. PR/38: Null check for jobs (Kurt Miller) 82. Fix spelling correction correcting ./foo -> ../foo2 (jean-luc leger) 81. PR/120: string0 in filetest does not have enough space. 80. V6.17.06 - 20110415 79. PR/110: Add $anyerror to select behavior. Default to the new one. 78. Don't try to spell commands that are correct (Rouben Rostamian) [./tcsh -f; set path=($path 2); mkdir foo2; cd foo2; touch foo; chmod +x foo; set correct=cmd; ./foo -> ../foo] 77. Don't push the syntax struct on the cleanup stack, because on foo;bar if foo fails, we will free bar prematurely (Ben Miller) 76. Avoid infinite loop while trying to print the pid of a dying process to a closed file (Bob Arendt) 75. Handle completion of ${ variables (Anthony Mallet) 74. Add --disable-nls-catalogs (Corinna Vinschen) 73. convert message catalogs to UTF-8 (Werner Fink) 72. check that the NLS path works before setting $NLSPATH. 71. use SYSMALLOC for GLIBC (Werner Fink) 70. use mallinfo for SYSMALLOC (Corinna Vinschen) 69. V6.17.05 - 20110201 68. Use mkstemp() if there for here docs (Werner Fink) 67. Fix handling of errors and exit values in builtins (Werner Fink) 66. Better pty name detection (Werner Fink) 65. Enable NLS catalogs on Cygwin (Corinna Vinschen) 64. NLSPATH handling fixes (Corinna Vinschen) 63. Avoid infrequent exit when tcsh cd's into a non-existent directory https://bugzilla.novell.com/show_bug.cgi?id=293395 (Werner Fink) 62. Don't try to spell check full path binaries that are correct because they can cause hangs when other nfs partitions are hung. (Werner Fink) 61. Avoid nested interrupts when exiting causing history writing to fail https://bugzilla.novell.com/show_bug.cgi?id=331627 (Werner Fink) 60. Instead of giving an error or ignoring lines with missing eol at eof, process them. 59. Avoid leaking fd's in mail check (Werner Fink) 58. Add cygwin_xcrypt() (Corinna Vinschen) 57. Recognize i686 (Corinna Vinschen) 56. Rename cygwin32 to cygwin and bring it up-to-date with modern cygwin settings (Corinna Vinschen) 55. Avoid double slashes in cdpath (Corinna Vinschen) 54. V6.17.04 - 20110118 53. Revert PR/110, breaks the test suite. 52. V6.17.03 - 20110117 51. PR/102: Complain on input files with missing trailing \n 50. PR/104: If atime == mtime we don't have new mail. 49. PR/113: Don't allow illegal variable names to be set. 48. PR/112: don't set $REMOTEHOST on the local machine. 47. PR/110: exit status of the pipeline should be the status of the last command. 46. Android support (Corinna Vinschen) 45. Add AUTOSET_KANJI which works around the Shift-JIS encoding that translates unshifted 7 bit ASCII (Werner Fink) 44. Handle mb{r,}towc() returning 0 by setting the return value to NUL (Jean-Luc Leger) 43. PR/109: make wait interruptible (Vojtech Vitek) 42. resource limit fixes: signed vs. unsigned, megabyte issue, doc issues (Robert Byrnes) 41. remove .bat and .cmd handling for executables on cygwin (Corinna Vinschen) 40. Don't echo history while history -L or history -M 39. Check for EOS before ** from Greg Dionne 38. Don't fork in backeval from Bryan Mason 37. Better globstar support from Greg Dionne 36. Error out when processing the last incomplete line instead of silently ignoring it (Anders Kaseorg) 35. Fix SEGV from echo `` 34. Better fixes for histchars and promptchars (nargs) 33. Fix win32 issue calling fmalloc/ffree from non-thread-safe context. (Fabio Fabbri) 32. V6.17.02 - 20100512 31. PR/79: nargs: Better handling for promptchars. 30. PR/97: Add parseoctal to retain compatibility with previous versions (Jim Zajkowski) 29. PR/84: Performance fixes for large history merges (add hashtable (Ted Anderson) 28. Revert previous #23; people should use $histlit if they want this feature. 27. Don't kill "hup" background jobs when a child of the shell exits. From Debian. 26. Ignore \r\n in the command line options for OS's that don't strip these from #!; from Debian 25. Fix enhanced missing patch (Greg Dionne) 24. Callers of rt_mbtowc don't grok -2 as a return. Return -1 for now. (Corinna Vinschen) 23. Turn HistLit on while recording history to avoid \!\! losing its \. From Debian 22. set autoexpand; set histchars="";\n crash. From Debian 21. V6.17.01 - 20100506 20. unset verbose while we are reading the history file to avoid echoing to the terminal. (Jeffrey Bastian) 19. globstar addition, Enhance addition, euid, euser, gid variables (Greg Dionne) 18. Make 'e' in vi mode work like 'b' - use wordchars (Alistair Crooks) 17. Handle UTF-16 surrogates (Corinna Vinschen) 16. Make tcsh work on systems where sizeof(wchar_t) == 2 (Corinna Vinschen) 15. Better support for Solaris >= 2.9 (Thomas Uhle) 14. Change internal expression calculations to long long so that we can deal with > 32 bit time, inodes, uids, file sizes etc. 13. Add new linux resource limits. 12. Don't print 'Exit X' when printexitvalue is set in `` expressions (Jeff Bastian) 11. Add more LS_COLORS vars (M.H. Anderson) 10. Reduce whitespace in Makefile (Don Estabrook) 9. Manual page fixes (Alan R. S. Bueno) 8. Remove history in loops bug from the documentation (Holger Weiss) 7. Add autorehash (Holger Weiss) 6. Add history.at (Ted Anderson) 5. Better NLSPATH handling (Norm Jacobs) 4. Fix hostname building from utmp (Cyrus Rahman) 3. Handle pending signals before flush so that the the history file does not get truncated. (Ted Anderson) 2. Fix AsciiOnly setting that broke 8 bit input. (Juergen Keil) 1. remember to closedir in mailchk (from Werner Fink, reported by David Binderman) 21. V6.17.00 - 20090710 20. Fix dataroot autoconf issue. 19. Fix directory stuff for unit tests. 18. Fix small bug in history in loops. 17. Provide newer config.{guess,sub} 16. Fix gcc 4 warnings. 15. Fix memory trashing bug introduced in 10. 14. V6.16.01 - 20090624 13. add missing sigemptyset in goodbye() 12. add rlimit swapsize from FreeBSD. 11. restore behavior where a[n-] never prints an error. 10. always save the whole command, not just the first 80 chars of it. 9. fix short2str/short2qstr length adjustment in wide chars (Vitezslav Crhonek) 8. set histfile=/tmp/history.temp; set savehist=(100 merge); alias precmd history -S. After that justpr is not restored and commands don't execute. (Andriy Gapon) 7. Fix "as" $ modifier from corrupting memory. set t=demfonsftraftionf; echo $t:as/f// 6. Make $% work with environment variable (Ron Johnston) 5. Dragonfly script support (Matthias Schmidt, m65) 4. Add autoexpand=onlyhistory (Don Estabrook, m66) 3. Add history in loops (Laurence Darby, m48) 2. Add missing colorls "rs" variable (Shlomi Fish, m70) 1. Fix pts detection issue (Ruslan Ermilov) 33. V6.16.00 - 20080930 32. longjmp clobbered variable fixes. 31. __GNX__ addition 30. Windows fork fixes 28. V6.15.02 - 20080830 27. Fix an eval free'ing botch (Per Hedeland) 26. /bin/echo "`" coredumped because stderror() calls longjmp corrupting the stack (Mark Davies). We should vet the code for all stderror() calls that cleanup local stack variables. 25. foo > ${undef} caused coredumps because of vfork() child corrupting the state of the parent stack. 24. $x[A-B] did not complain for A out of range. (Cai Xianchao) 23. rename setp -> tcsh_setp to avoid conflict with mach (Javier Vasquez) 22. Fix degree handling by defining __XPG4_CHAR_CLASS__ for solaris (Mike Sullivan) 21. Change 'od' to 'od -c' in tests so that they work on big endian machines. (Martin Kraemer) 20. Add environment variable COMMAND_LINE to be available in completions (Marcin Konarski) 19. V6.15.01 - 20070928 18. Evaluate expressions in the proper order (Li Zefan), controlled by compat_expr. 17. Don't need to flush() if we are silent. Prevents recursive error issue. (joshua stein) 16. Don't execute the jobcmd if the output is not a tty (Charles Ross) 15. Quote the history in the examples (Johann 'Myrkraverk' Oskarsson) 14. Mismatch clarification patch (Per Hedeland) 13. Fix 'repeat n cmd &' abort() (Mike Sullivan) 12. Fix octal parsing (Li Zefan) 11. Fix pty detection for autologout setting (Kris Kennaway, Giorgos Keramidas) 10. kill `foo` got stuck because sigchld was disabled too soon (Mark Peek) 9. Avoid null pointer dereference in proc cwd (Kurt Miller) 8. eval "foreach a b c" exits (Anthony Menasse) 7. Quoting was broken in substitutions (Joe Wells) 6. QNX patches via pkgsrc 5. cd - twice from a directory that contained a glob pattern, expands the glob twice (Mark Santcroos) 4. MidnightBsd support (Lucas Holt) 3. Fix history substitution core-dump with no history entries 2. Merge two character tables that are the same (Martin Kraemer) 1. On ancient 7 bit locales, punctuation characters are used to denote special characters such as umlaut, adiaresis, etc. These characters return true for isalpha/isalnum. Ignore them because they break parsing (Martin Kraemer) 74. V6.15.00 - 20070303 73. fix extension eating windows code (christos) 72. fix loop in %R history expansion (christos) 71. sched +X source file disables interrupts (Mike Sullivan) 70. One off copying macro buffers (Jean-Luc Leger) 69. Avoid infinite loops in :ga modifiers when the LHS is a substring of the RHS. 68. Automatically disable WIDE_STRINGS with --disable-nls (Miloslav Trmac) 67. V6.14.07 - 20060825 66. rename set to setv to avoid clashes (christos) 65. Eliminate sighold/sigrelse (christos) 64. Compilation cleanups. (Martin Kraemer) 63. Don't rebuild needlessly when generated files are unchanged (Martin Kraemer) 62. Fix confusion between Char and eChar (Martin Kraemer) 61. V6.14.06 - 20060824 60. set PROGRAM_ENVIRONMENT for OSD_POSIX (Martin Kraemer) 59. EBCDIC patch (Martin Kraemer) 58. Remove globbing support in history rearches (Ryan Barrett) 57. Highlighting patch (Ryan Barrett) 56. Mark-Cursor exchange emacs editing fix (Martin Kraemer) 55. V6.14.05 - 20060304 54. don't limit termcap strings to 1K (Alan Ferrency) 53. protect against null path. 52. Be more conservative in wide_read PR#29 51. remove HAVE_STRCOLL; not needed anymore (Miloslav Trmac) 50. remove imake support and make nls configurable by configure (Miloslav Trmac) 49. V6.14.04 - 20060214 48. Fixes build failure with !SHORT_STRINGS (Miloslav Trmac) 47. Fixes constness warnings with !SHORT_STRINGS: short2str is "strip()", which modifies its argument. Rather than verify this is safe in all callers, I have added caching_strip() to mirror the behavior of other modes. (Miloslav Trmac) 46. Fixes some constness warnings with SHORT_STRINGS; this is orthogonal to both build errors with SHORT_STRINGS in 6.14.03, and short_strings.patch. (Miloslav Trmac) 45. config-catalogs.patch: Autodetect catalog support (#14). (Miloslav Trmac) 44. auth.patch: Handle false positives in getauthid() detection, IIRC on FreeBSD. (Miloslav Trmac) 43. crypt.patch: Autodetect whether is available. (Miloslav Trmac) 42. sigint.patch: Block SIGINT while waiting on children. This is necessary, but I'll still have to look on the older releases to find out why it was not necessary before ("before" = 6.13 on Linux). (Miloslav Trmac) 41. config-package.patch: Readd package version to configure.in, the testsuite uses it. (TODO: a better integration with package.h) (Miloslav Trmac) 40. item_len.patch: Truncate item.len when truncating the string, just to be sure. (Miloslav Trmac) 39. va_list.patch: Add missing va_end(). (Miloslav Trmac) 38. Use va_copy in xvasprintf(); emulate va_copy as suggested in Autoconf manual if it is not available. (Miloslav Trmac) 37. declarations.patch: Replace some #ifdef nests by autodetecting whether the guarded declaration is necessary. I'm only guessing this is the intent of the #ifdefs, though. Declare environ unconditionally, it is a bit hard to detect and the declaration is correct for Linux, anyway. (Miloslav Trmac) 36. warning.patch: Fix a const warning. 35. More color-ls variables (Jean-Luc Leger) 34. V6.14.03 - 20060212 33. Signal related changes (Miloslav Trmac) 32. Misc cast cleanups and code clarification (Miloslav Trmac) 31. Use dynamically allocated buffers everywhere (Miloslav Trmac) 30. Fix reading of invalid byte sequences (Miloslav Trmac) 29. read should only increment count when it succeeds (Miloslav Trmac) 28. testsuiteadditions (Miloslav Trmac) 27. -n fixes (Miloslav Trmac) 26. signness warnings (Miloslav Trmac) 25. Prevent infinite recursion in catclose (Gerhard Niklasch) 24. Add Dragonfly (Joerg Sonnenberger) 23. Check for wcwidth returning a negative number (TOMITA Yoshinori) 22. recognize "li" and "co" in echotc (Martin Kraemer) [PR/7] 21. Don't use T_Cols for wrapping purposes; use TermH consistently (Martin Kraemer) [PR/8] 20. Don't display duplicate utmp records (Miloslav Trmac) [PR/17] 19. Clarify hashing and -f sections of man page (Volker Quetschke) [PR/20] 18. Dave Yearke: configure did not recognize solaris 10 [PR/18] 17. CYGWIN: Don't lowercase commands and allow foo.exe on command line. (Corinna Vinschen) [PR/19] 16. CYGWIN: cd /foo && cd .. ends up erroneously in // for systems that HAVE_SLASHSLASH [PR/21] 15. %j was broken (Peter Kruse) 14. Extend // handling in pathnames from being apollo specific to also cygwin (Corinna Vinschen) 13. Fix uppercase/lowercase transformations (Jean-Luc Leger) 12. Fix symlink expansion (revert from 6.10.00) (Jean-Luc Leger and Christos) 11. Fix literal sequence in prompt (Miloslav Trmac) 10. V6.14.02 - 20050412 9. Prototype fixes (Miloslav Trmac and Jean-Luc Leger) 8. V6.14.01 - 20050411 7. Make =- refer to $owd (H.Merijn Brand) 6. Use prototypes (almost) everywhere (Miloslav Trmac) (issue #5) 5. Fix doc path in Cygwin installation (Corinna Vinschen) 4. Fix column size calculation (Martin Kraemer) 3. Implement newline-and-hold and newline-and-down-history (Per Hedeland) 2. Messages fixes for the ja (Japanese) locale (NAKAMURA Takeshi) (issue #4) 1. Enable charset conversion in nls (Miloslav Trmac) (issue #3) 76. V6.14.00 - 20050325 75. Additional messages for the ja (Japanese) locale (NAKAMURA Takeshi) 74. V6.13.10 - 20050321 73. Implement --help (Tom Warzeka) 72. Clamp solaris autologout time 71. Add support for "limit heapsize" (Martin Kraemer) 70. Improved autoconf for utmp/utmpx (Miloslav Trmac) 69. V6.13.09 - 20050303 68. Autoconf additions (Miloslav Trmac and Jean-Luc Leger) 67. Fix erroneous sign extension when printing huge numbers (Martin Kraemer) 66. Handle delay in termcap entries (Martin Kraemer) 65. EBCDIC and BS2000 fixes (Martin Kraemer) 64. Avoid the SCCS percent-S-percent sequence differently (Kimmo Suominen) 63. Mark position update fixes (Martin Kraemer) 62. Define BSDLIMIT and BSDTIMES for Cygwin (Corinna Vinschen) 61. Fix NLSFrom prototype (Martin Kraemer) 60. V6.13.08 - 20050303 59. Add test for socklen_t in autoconf 58. Fix problems with NLSFrom prototype 57. Deal with AIX's utmpx lossage 56. V6.13.07 - 20050118 55. constifications, XXX: duplicate prototype on setpgrp(). 54. iconv fixes (Miloslav Trmac) 53. literal code cleanups (Miloslav Trmac) 52. move intptr_t definition to sh.h and include both stdint.h and inttypes.h 51. Fix dup2() issues, and introduce FSAFE as the highest file descriptor to be left untouched. This is to avoid closing hesiod/nis etc file descriptors behind their backs. I need to really fix file descriptor handling one day. 50. forward<->reverse char conversion verification (Miloslav Trmac) 49. japanese locale additions (Yoshiyuki Sakakibara) 48. wide character/utf 8 cleanups (Michael Schroeder) 47. V6.13.06 - 20050105 46. autoconf cleanup (Miloslav Trmac) 45. Fix literal refresh code (Michael Schroeder) 44. Fix history printing bug with WIDE_STRINGS 43. V6.13.05 - 20041121 42. deal with not having intptr_t 41. get rid of bool. 40. Protect HAVE_ICONV with NLS_CATALOGS 39. Deal with wchar_t being unsigned. 38. If we HAVEUTMPX redefine some utmp functions in terms of utmpx ones. 37. V6.13.04 - 20041120 36. Fix display problems with two-column characters when using WIDE_STRINGS. (Miloslav Trmac) 35. Change charset declarations for some of the translations. (Miloslav Trmac) 34. Set O_LARGEFILE properly (Miloslav Trmac) 33. Use libc functions to access utmp data (Miloslav Trmac) 32. t_pmatch() was not really case-insensitive when cs == 8 (Miloslav Trmac) 31. Fix escaping of "control" bytes 0x80..0x9F when they are actually parts of UTF-8 character representation. (Miloslav Trmac) 30. V6.13.03 - 20041120 29. More close_on_exec fixes (Miloslav Trmac) 28. SUSE dspmbyte fixes (via Harald) 27. Newline handling in command substitution controlled by csubstnonl (Miloslav Trmac) 26. Fix UNC prompt expansion (WIN32) (Yasuhiro Matsumoto) 25. Fix incorrect xprintf() usage (Miloslav Trmac) 24. Additional architectures support for $HOSTTYPE and $MACHTYPE (Miloslav Trmac) 23. Use nl_langinfo(CODESET) to determine $dspmbyte (Miloslav Trmac) 22. Complete arguments that contant a # (Steven Grady) 21. Set close-on-exec in subshells 20. Compilation fixes (Miloslav Trmac) 19. V6.13.02 - 20040804 18. de-register, de-extern, de-shadow, and const poison. 17. WIDE_STRINGS support (Miloslav Trmac) 16. warning cleanups (Miloslav Trmac) 15. nlsclose() arg passing, exp2 is now a gcc builtin (Mark Peek) 14. iconv malloc portability fix. 13. make automatic dspmbyte determination case insensitive 12. V6.13.01 - 20040724 11. bug fix in automatic dspmbyte setting (Miloslav Trmac) 10. iconv patches (Miloslav Trmac) 9. cygwin signal fixes; only init 32 signals, don't set bit flag on error; breaks SIGHUP. (Corinna Vinschen) 8. Make sure terminal size change takes effect immediately. 7. Obey $printexitvalue for builtins. 6. FreeBSD PR/66420: Allow history parsing to be special so that it can handle inline # characters (Oliver Eikemeier) 5. new termname builtin from (Andrew Stevenson) 4. if ($var =~ *[^0-9]*) echo not numeric, fix (Andrew Stevenson) 3. completion fix to avoid dup hosts from ssh_known_hosts (Eric D. Hendrickson) 2. vc++ 7.1 compilation fix (Steve Schockley) 1. UTF-8 handling for both single and double width characters, but no combining character support. (Michael Schroeder) 36. V6.13.00 - 20040519 35. V6.12.03 - 20040322 34. turn on kanji and dspmbyte by default; add check for utf8 locales, and turn parsing of that automatically based on $LANG. 33. Fix compilation issue under Windows/NT and charset incorrect patch (Yoshiyuki Sakakibara) 32. completion additions (Tom Warzeka) 31. compilation fix (Martin Kraemer) 30. V6.12.02 - 20040221 29. Glob completion listing addition (Tom Warzeka) 28. BS2000 bs2cmd builtin. (Martin Kraemer) 27. Fix interrupt resetting code when /etc startup scripts have syntax errors (Mark A. Grondona) 26. Clarification of kill-ring commands (Per Hedeland) 25. Debian completion additions (Martin Godisch) 24. Japanese character set fixes (Juehiro-san) from debian 23. NLS charset fixes; disabled since they only work with gnu gencat (Martin Godisch) 22. Fix HPUX >= 11 resource (Jack Cummings) 21. Handle breaksw that jumps out of loops. 20. Revert #16. It causes worse problems. 19. Avoid using execl() because the last NULL does not always promoted to a pointer because the function is variadic (Harti Brandt) 18. revert ignoreeof to the 6.11.00 behavior and document it (Martin Godisch) 17. do a case insensitive comparison for the multibyte vars (Martin Godisch) 16. don't sigsuspend() for an already exited job 15. glob all arguments in source (Martin Godisch) 14. various debian fixes (Martin Godisch) 13. setenv syntax check revert (Satoshi I. Nozawa) 12. EAGAIN typo (dan harkless) 11. filec compilation issue on hpux (beebe) 10. win32 compilation fixes for O_LARGEFILE (amol) 9. Don't go into an infinite loop when tcgetpgrp() returns an error. 8. Cygwin fixes (Corinna Vinschen) 7. NLS catclose() bug avoidance (KAJIMOTO Masato) 6. V6.12.01 - 20030208 5. Misc NT cleanup. No more GPL code (amol) 4. use strtol() to detect errors in builtin kill (Peter Jeremy) 3. Recognize linux systems on mips* (Maciej W. Rozycki) 2. Enable complete=igncase on unix (Stephen Krauth) 1. Eliminate maxitems (Todd Miller) 58. V6.12.00 - 20020732 57. misc cleanups. 56. V6.11.05 - 20020712 55. We should have socklen_t in the INET6 case, but we leave int otherwise. 54. Fixed for Darwin/Rhapsody (Mark Peek) 53. provide new config.sub and config.guess from ftp://ftp.gnu.org/gnu/config/ (Nelson Beebe) 52. V6.11.04 - 20020709 51. Dissallow setting of environment variables that do not contain alphanumeric names (Ton Voon) 50. Don't expand path components that don't resolve to path names (Jonathan Chen) 49. Make $ignoreeof agree with the man page (Matias Moreno Meringer) 48. Fix argument passing in tc.prompt.c expdollar (Nelson Beebe) 47. Completion fixes from FreeBSD (Mark Peek) 46. FreeBSD's sbsize limit (Mark Peek) 45. Fix repeat 3 repeat 2 echo foo 44. Fix I/O redirection in scripts (Ian D Allen) 43. Fix ` \\\n ` evaluation (Jean-Luc Leger) 42. add --version (Nelson Beebe) 41. portability fixes for sed (Nelson Beebe) 40. undef sv_handler for AIX's benefit. (Nelson Beebe) 39. Add a test for ss_family that disables IPV6 (linux/ppc, osf/1 have problems) (Nelson Beebe) 38. Disable ipv6 for Apple (Nelson Beebe) 37. Fix darwin configure entry (Nelson Beebe) 36. V6.11.03 - 20020701 35. Add Ian D. Allen's bug list. 34. If we are invoked as csh, default to bsd echo (Matej Vela) 33. Don't close file descriptors too early because setuid scripts fail. (Jill Pryse-Davies) 32. Completion updates (Tom Warzeka) 31. Fix compilation issue on SunOS4 with _POSIX_VDISABLE (Tom Warzeka) 30. ukrainian update (Olexander Kunytsa) 29. DSPMBYTE=utf8 patch (Jean-Luc Leger) 28. fix ipv4 only compilation, remove extra sigsetmask() call (Takayuki Nakao) 27. window change can cause free to be re-entered causing abort (Mark Peek) 26. vp->vec vetting, suggested by Ian Dall. 25. V6.11.02 - 20020516 24. Fix prompt bugs in $var and %c0n (TAKAI Kousuke) 23. Add Cray SV2 config (Rafal Maszkowski) 22. Add pdf manual page (Warren Ferguson) 21. Fix REMOTEHOST lossage in with AF_LOCAL sockets (Tom Mander) 20. Fix win32 break because of TIOCSTI (Amol) 19. Fix TIOCSTI for hpux 11 (Igor Schein) 18. Avoid collapsing paths that refer to non-existent components (Martin Kraemer) 17. Make -shell invocations always treated as a login shell. 16. V6.11.01 - 20020308 15. Fixes to polish nls locale (Paweł Niewiadomski) 14. russian locale fixes (Alexey Dokuchaev) 13. document door support (Shaen) 12. tcsh euc handling extensions (Alexey Zelkin) 11. Make sure that jobcmd does not clobber the current job (Rob McMahon) 10. Make sure that the output of verbose and echo do not end up in the command output (Victor I. Pasko) 9. Add %j in prompt [needs more work; is not right the first time after proclist changes] (Hr. Peter Kruse) 8. `` commands with embedded newlines would ignore commands following the new line (Victor I. Pasko) 7. Re-initialize nls if NLSPATH is changed (Naoki Wakamatsu) 6. Fix 64 bit compilation with linux and resource limits. 5. Avoid double globbing when ls-F needs to fork (Joe Townsend) 4. put back csh filec compatible support. 3. add support for quads in xprintf in the presence of gcc. 2. unlimit should set rlim.rlim_max to rlim.rlim_cur if max < cur. 1. Make ~user work again when the home directory is '/'. 40. V6.11.00 - 20010902 39. Completion fixes (Tom Warzeka) 38. make c_insert not static so that it can be used from win32 (amol) 37. Fix rmstar not to corrupt memory when we say no. (Mark Peek) 36. V6.10.02 - 20010806 35. polish nls locale (Paweł Niewiadomski)) 34. Fix a tcsetattr race running background jobs as the last line on an xterm window (Andrew Brown) 33. jobcmd alias (Greg Parker) 32. hpux11 support (Joshua Weage) 31. Fix SHORT_STRINGS compilation (Daniel Trinkle) 30. Add kill -s (Mark Peek) 29. Don't recognize all mips as dec (Bjorn Knutsson) 28. Fix GLOB_QUOTE problem (noted by Per) I introduced in 6.10.01. 27. Port to concurrent's powermaxos (Matt Majka) 26. New builtin srcfile (Amol) 25. Fix bindkey "\\" cmd (reported by Ismail H. Tuncer) 24. %0Xc was broken in the prompt if the user's home directory was '/'. (reported by Edward Glowacki) 23. V6.10.01 - 20010426 22. LARGEFILE support on Linux. 21. Add big5 multibyte support (Yen-Ming Lee) 20. Check the return value of setpriority (Dima Dorfman) 19. Avoid constructing paths with // on DomainOS (Nickolai Zeldovich) 18. Russian translation (Ilmar S. Habibulin) 17. Fix hostdefs for alpha support in FreeBSD (Andrey A. Chernov) 16. Add door support in colorls (Shaen) 15. Add BSD_STYLE_COLORLS for FreeBSD (Anand) 14. MAXHOSTNAMELEN needs to be 256 (Kris Kennaway) 13. Document stty -tabs problems on compaq (Nelson H. F. Beebe) 12. Fix broken comment, and new versions of config.guess and config.sub (H.Merijn Brand) 11. fix redrawing in the recognize case (Andrew Brown) 10. don't call qsort with 0 items. (Luke Mewburn) 9. fix echo;echo;echo; not outputing anything (Andrey A. Chernov) 8. Fix shell word parsing in dabbrev-expand (Per Hedeland) 7. hpux fixes (Chienting Lin) 6. Implement kill ring (Per Hedeland) 5. Avoid core-dumping when a very long $HOME gets passed in (Kris Kennaway) 4. Add rlimit_vmem for linux based on rlimit_as (N KomaZaki) 3. back out symlink=expand path check. 2. Add Estonian translation (Toomas Soome) 1. Accept empty $savedirs to mean infinity. 57. V6.10.00 - 20001119 56. Completion fixes (Tom Warzeka) 55. add missing linux kanji define (Tsuyoshi Kawabe) 54. More WINNT_NATIVE fixes (amol) 53. Fix compile error on winnt (Yoshiyuki Sakakibara) 52. nonstopux configure and makefile fixes (Tom Bates) 51. V6.09.04 - 20001111 50. Order of initialization for multibyte display was wrong (HyunChul Kim) 49. Follow the guideline for linux for japanese locale: http://www.linux.or.jp/JF/JFdocs/Japanese-Locale-Policy.txt (Tomohiro KUBOTA) 48. Ukrainian nls map (Olexander Kunytsa) 47. exit immeditiately if we get an error while we are setting up (Michael Shalayeff) 46. (unset path; unsetenv PATH; rehash) -> crash (Kent Vander Velden) 45. change winnt to winnt_native (Randolph Fritz) 44. Support home/end in the editor (Andrey A. Chernov) 43. Typo s/gycwin/cygwin/ in tc.os.h (Andreas Schott) 42. Alpha ev6 addition (Karen R. McArthur) 41. DSPMBYTE patch from (Issei Suzuki) 40. Security fix for here-doc tmp files (proton@ENERGYMECH.NET) 39. Fix resource limit rounding *again* (Johannes Gross) 38. Fix $ expansion in prompt (Takashi Sumiyoshi) 37. V6.09.03 - 20000715 36. cygwin port fixes (Arihiro Yoshida) 35. Add a new "catalog" variable that specifies which NLS catalog to be used (Issei Suzuki) 34. cleanup and addition of page up/down (amol) 33. fix vfork compile problem. 32. use inet_addr instead of inet_aton for portability. 31. V6.09.02 - 20000704 30. lots more completions (George Cox) 29. change FILSIZ to BUFSIZE [now that BUFSIZE >> MAXPATHLEN] and avoid a potential buffer overflow in sh.dir.c (Volker Schmidt) 28. _MINIX_VMD port (Martijn van Buul) 27. inet6 handling for remotehost and configure (Hajimu UMEMOTO) 26. aix-4 does not need gethostname (Darren Reed) 25. IBM OS/390 Unix Systems Services support (Peter Prymmer) 24. Fix prompt formatting (Andrey A. Chernov) 23. Use HostType from Imakefile correctly (Kjetil Torgrim Homme) 22. Handle long and expanded history lines better (Boleslaw Ciesielski) 21. With symlinks=expand expand valid paths only (Martin Kraemer) 20. Make one-byte charsets work with KANJI (Andrey A. Chernov) 19. NT-specific executable detection moved to NT code (amol) 18. New "complete module" (Dan Nicolaescu) 17. Correctly display scaling string in limit error messages (Nathan Ahlstrom) 16. Don't display "unset watch" message when not appropriate (kim) 15. V6.09.01 - 20000114 14. Circumvent IRIX4D ESTALE bug by exiting. 13. IRIS4D de-linting. 12. Finnish nls catalogs (Jukka A. Ukkonen) 11. Even more multibyte fixes (Taga Nayuta) 10. Patches to statically link tcsh under solaris-2.6 (John Hawkinson) 9. Manual page typos (R. Bernstein) 8. HP/UX-11 (9000/800) HP/UX-10.20 (9000/820) (Haflidi Sigtryggur Magnusson) 7. Color-ls fixes (Luis Francisco Gonzalez) 6. Don't re-use time0 to compare to stat's st_mtime, cause it could be the value returned from times(2) which is the ticks since system startup (Frank van der Linden) 5. Time percentage wrap fix (Simon Burge) 4. EUCKR support (HyunChul Kim) 3. Grammar and typo fixes for tcsh.man (Steve Kelem) 2. More multibyte fixes (Rodney Ruddock) 1. Change 6.08 -> 6.09 where I missed it; update for utlrix 4.5 (Simon Burge) 65. V6.09.00 - 19990816 64. Add csh emacs mode (Dan Harkless) 63. Make sure the the glob buffer matches the word buffer size (Brian Biswas) 62. Fix periodic to work without tperiod set (Kenny McCormack) 61. V6.08.07 - 19990813 60. Fix e_dabbrev_expand (Bjorn Knutsson) 59. Make \builtin work again (by calling the command not the builtin) 58. Add NLS_BUGS for OS's that keep file descriptors open for NLS (Ian Dowse) 57. Make NONLSREBIND work after tcsh starts up. 56. Fix AIX stupid exit bug. (Dan Harkless) 55. More irix fixes (Kaveh) 54. V6.08.06 - 19990701 53. module command completion (Dan Nicolaescu) 52. Man command completion enhancement (Tom) 51. Fixes for irix configuration (Kaveh) 50. Support \a and \e in echo command. From (Keith Thompson) 49. Alpha configure nit from Kaveh 48. V6.08.05 - 19990511 47. In some system, when a builtin fails immediately after we start tcsh and before we print any messages, NLS messages fail (some descriptor might be closed) [FreeBSD, HP/UX?]. So we force reading the NLS catalogs in errinit() (Yoshiyuki Sakakibara) 46. typos in ja/set1 and ja/set29 (Yoshiyuki Sakakibara) 45. "dspmbyte autoset trap" support to HP-UX 10.20. (Yoshiyuki Sakakibara) 44. misnumbered NLS message in tw.help.c (Yoshiyuki Sakakibara) 43. colorcat variable for NLS color messages (Yoshiyuki Sakakibara) 42. autoconf fixes from Kaveh (and if it broke, me) 41. SGI irix fixes from Glenn Coombs. 40. Don't spin writing the history file when quota is exceeded (Rob McMahon) 39. V6.08.04 - 19990419 38. complete additions (John Gotts) 37. Port to amiga with geek gadgets (Arto Huusko) 36. Ignore case in setenv for windows (amol) 35. Bug fixes for NT unc stuff (amol) 34. Hash spell check (amol) 33. V6.08.03 - 19990211 32. Helpcommand documentation (Vladimir Alexiev) 31. small Y2K fix [%y in prompt would be formatted as 10 not 00 at year 2000], and new Itoa() code (Chris Torek) 30. OpenBSD m68k patches (Paul DuBois) 29. Avoid redefinition of getpeername on Solaris-2.7 28. fix problems with savedups=erase, savehist=merge (Randy Gobbel) 27. Don't set $shell to csh, if we were invoked as tcsh (Tomas Persson) 26. added $_ 25. added postcmd 24. V6.08.02 - 19981124 23. document continue and continue_args 22. wrong test in slowexec made NT optimization a noop (Amol) 21. Rhapsody fixes + separate CFLAGS/CPPFLAGS (Wilfredo Sanchez) 20. literals in both prompt at rprompt did not work properly (Taga Nayuta) 19. multibyte deletion fixes (Yoshiyuki.Sakakibara) 18. clean up key binding stuff for NT by moving the nt dependent code to the NT source (amol) 17. dspmbyte fixes for AIX, typos in man page and nls (Yoshiyuki Sakakibara) 16. Convex fixes (Ron Echeverri) 15. V6.08.01 - 19981025 14. SX4 port; this adds many casts for machines where ptrdiff_t is 64 bits. (Andreas Schott) 13. U/Win port; does not work properly with U/Win 1.6, wait for the next version (Chris Jones) 12. Sgi does not need gethostname prototype (John Bogan) 11. Logic in sh.dir.c was wrong for NT (Amol) 10. HP_CXD_SPP stat64 fixes (Scott Garee) 9. Documentation spelling fixes (Keith Thompson) 8. Documentation fix for savedirs (Amol) 7. Siemens OSD_POSIX fixes (Martin Kraemer) 6. include for all the BSD's (Trevor Johnson) 5. Multibyte display fixes for gnu ls (Taga Nayuta) 4. Configure/Makefile hesiod and bindir fixes (Dan Winship) 3. Use winnt not win32 in tc.os.h (Amol) 2. Cygwin32 port (Raj Menon) 1. Ported Fixes (Tom) 95. V6.08.00 - 19981002 94. where builtin nt fix (Amol) 93. V6.07.13 - 19980926 96. NT multibyte fixes (Amol) 95. NeXT needs getcwd. 94. fix new bug introduced by strncpy'fication in sh.dir.c (Taga Nayuta) 93. V6.07.12 - 19980918 92. add cray in configure script (Tom) 91. nls fixes 90. Avoid buffer overflows in directory code (kim) 89. Add multibyte character display support (Yoshiyuki Sakakibara) 88. Make tcsh use getcwd instead of getwd and supply a getcwd. 87. Fix remotehost again (kim) 86. V6.07.11 - 19980913 85. Leave remotehost ip addresses alone (don't try to resolve them) (Kim) 84. Read vs. force_read fixes (Amol) 83. Make colorls if color is set before the first LS_COLORS setenv (Taga Nayuta) 82. Use _PATH_DEFPATH on BSD4_4 systems. (Jim Bernard) 81. Cursor bounds checks (Michael Schroeder) 80. Syntax error nits (Michael Schroeder) 79. configure fixes (Michael Schroeder) 78. V6.07.10 - 19980904 77. Off by one error in NO_ERRORS... 76. Japanese NLS messages (Yoshiyuki Sakakibara) 75. Add ENXIO and EBADF in the test for write failures for Irix Zombies (Ralf W. Grosse-Kunstleve) 74. Missed a test for NOSTRCOLL in glob.c (Michael Liepelt) 73. Another ABSOLUTEP change (Amol) 72. Italian NLS messages (Massimo Bertozzi) 71. WIN32 fixes for ntslowexec and color_ls literal printing in prompt (Amol) 70. a few missed WIN32 merges (Amol) 69. defined YPBUGS for sgi (Kaveh) 68. V6.07.09 - 19980707 67. Separate the nt builtins (Amol) 66. completion fixes (Tom) 65. color ls fixes (Taga Nayuta) 64. V6.07.08 - 19980629 63. add config.h.in (Kaveh) 62. win32 updates (Amol) 61. warning and portability cleanups on the new changes (Kaveh) 60. V6.07.07 - 19980628 59. Fast execute by-pass for win32 (Amol Deshpande) 58. Clean up const usage a bit, and fix gcc 2.8.1 warnings. 57. Use @bindir@ for DESTBIN in Makefile.in (Edgar Hoch) 56. Avoid overflow in time builtin computation (Nobue Adachi) 55. Color ls additions (Taga Nayuta) 54. unsigned char vs. char warning fixes (Kaveh) 53. Solaris 64 bit fixes (fix directory offset bug) (Thomas-Martin Kruel) 52. More win32 patches (Amol) 51. autoconf lossage from (Kaveh) 50. V6.07.06 - 04/08/98 49. Collation fix for globbing (Andrey A. Chernov) 48. We might have NLS_CATALOGS and not LC_MESSAGES (Andrey A. Chernov) 47. 4.4BSD header fixes (Andrey A. Chernov) 46. Signed char prompt fixes (Andrey A. Chernov) 45. Pattern match fix for directory searches (Mike Patnode) 44. Pentium DGUX fixes (Miko Nahum) 43. Spanish nls message catalogs (Luis Francisco Gonzalez) 42. Fix trailing whitespace parsing in HASHBANG code (Martin Kraemer) 41. Remove stray debuggin message from unmatched substitutions. (from Amol Deshpande) 40. Fix reversed arguments in Usagae message. (from Amol Deshpande) 39. Fix bug introduced at tcsh-7.06.03 [expdollar] that affected %.n prompt format. 38. Fix typos introduced in last batch of changes. 37. Fix interrupted script using onintr, exiting parent shell problem. 36. Cleanup prototypes. 35. V6.07.05 - 10/28/97 34. Integrate Amol Deshpande's WINNT fixes to the tcsh source. Note that this is not complete yet; we are missing the NT glue code and the message catalogs. 33. Fix ^T at the first character in the line (Chuck Silvers) 32. Eliminate xsprintf and xvsprintf 31. Qmail patch from (Matthew Zahorik) 30. Added missing linux signals (Vadim Vygonets) 29. fixed problem where complete complete 'p/*/t:*.txt/' would not honor the pattern. 28. Port to an EBCDIC machine: BS2000 by Siemens Nixdorf that has an IBM/390 compatible processor (Martin.Kraemer) 27. Detect when we have errors writing to stdout (Vadim Vygonets) 26. Ignore quotes in the comparisons for builtins, so that \builtin works (Amol Deshpande). 25. HPUX, portability fixes; make sure that we have the right config file (Jonathan Kamens) 24. Don't do lookups for x displays and figure out ttys properly (Leonard N. Zubkoff) 23. make print_by_columns print in a single column when the output is not a tty 22. use rlim_t for Solaris2 (Casper Dik) 21. V6.07.04 - 05/04/97 20. set -f -l patch (Michael Veksler) 19. SGI patches (Tomasz J. Cholewo). Also fix completion code to take into account aliases that start with a period. 18. SCO patches (Boyd Lynn Gerber) 17. Fujitsu patches (Toshiaki Nomura) 16. autoconf patches (Kaveh Ghazi) 15. BSDI patches (Paul Vixie) 14. %Q formatting character addition. 13. Fix set=#123; echo $i:s/#// (Quoting problem) 12. V6.07.03 - 02/23/97 11. Understand %$variable in the prompt. 10. Quote directory names properly in .cshdirs 9. USE_ACCESS and autoconfig patches from (Larry Schwimmer) 8. Pyramid att config file (Andrew Lister) 7. $rprompt code (Luke Mewburn) 6. Kanji patches (Huw Rogers) 5. Cray T3E port (Jorn Amundsen) 4. Avoid html redirects in tcsh.man2html (from Kimmo) 3. HP/UX 10.0 fix for filesize resource limit; don't scale by 512 anymore. 2. Workaround for TIOCSTAT for NetBSD from lukem@netbsd.org 1. Return exit status from 0..255 not -128...127, as POSIX mandates. V6.07.02, 10/27/96 58. More configure fixes from Kaveh. 57. Fix histdup=erase again: Don't renumber events, or access uninitialized storage. 56. 6.07.01 - 10/19/96 55. Fix histdup=erase, where after some repetitions, we would get negative history events (kim@gw.com) 54. NLS fixes and typo in sh.err.c (Martin.Kraemer@deejai.mch.sni.de) 53. Output history in raw format in the history file (mveksler@VNET.IBM.COM) 52. Fix possible core dump when !: in autoexpand mode (rbrown@ERA.COM) 51. 6.07.00 - 10/11/96 50. Avoid stdio.h inclusion problem in SCO (gethost.c). 49. A bit of housekeeping in host.defs 48. 6.06.04 - 10/05/96 47. Fix tellmewhat() code to return true if found. 46. Change register foo to register int foo to avoid compiler warnings. 45. Fix problem with sticky non editing mode from Casper Dik. 44. history lex fix from Martin Kraemer; history events that ended with 0 were not properly parsed. 43. SNI fixes from Martin Kraemer. 42. SGI fixes from Ralf W. Grosse-Kunstleve. 41. BSDI2.1 fixes from Paul Vixie. 40. 6.06.03 - 09/24/96 39. undef TIOCGLTC for HP/UX 10.0 from Michael Shroeder 38. Sinix fixes 37. 6.06.02 - 06/22/96 36. Added implicitcd 35. Added configure.in and Makefile.in from Kaveh. 34. unset path, unsetenv PATH, ./foo did not work. 33. Add VAR_NOGLOB, and use it to avoid globbing directory names when cd'ing into them. 32. Fix bug introduced in the new tty parsing code. 31. Avoid pushing string back to the parsing string in ${ errors. 30. Patches for the manual page from Dave. 29. 6.06.01 - 05/24/96 28. Use sysconf to get NCARGS if available Robert Daniel Kennedy 27. Grab the program name and use that instead of tcsh in error messages. 26. Fix histdup, so that it does not leave gaps in the event sequence. 25. HP/UX v10.0 fixes: Don't use bsdtty.h and avoid clobbering memory since SIGRT??? is defined as -1. 24. Avoid coredumps when $TERMCAP exceeds 1024 characters Michael Schroeder 23. Fix memory clobbering when SHORT_STRINGS is not defined. Todd J Derr 22. Only restart stopped editors. Robert Webb 21. Recognize pts sysv ptys when checking to set autologout Bob Myers 20. Magic space incomplete modified core dump fix. Chris Metcalf , Bradley White 19. Linux nls fixes Rik Faith 18. SGI RS8000, Ported notes. Ralf W. Grosse-Kunstleve 17. Greek nls messages. Aggelos P. Varvitsiotis 16. Imakefile linux and libcrypt fixes. Jonathan Kamens 15. FreeBSD fixes Jukka Ukkonen 14. Expand the environment space for path Steve Kelem 13. Don't overwrite the environment randomly Steve Kelem 12. Don't turn the editor on when we have dumb or unknown terminals. This breaks emacs when compiled with terminfo Jonathan Kamens 11. Fix F- parsing in tc.bind.c (Bob Meyers) 10. Added -T option in history to force timestamp printing. -h alone does not print timestamps anymore for compatibility with csh. 9. Typo in tc.bind.c [with -DOBSOLETE] (misplaced parenthesis) 8. Recognize convex models properly. 7. suppress the DING! option using the noding variable. 6. negative nice values did not work. 5. Harris CX/UX 7.x support. 4. ERR_DMMODE was used on the crays but not defined. I changed the error messages in tc.os.c to use ERR_STRING instead, and fixed a missing error message in the catalogs. Someone will need to retranslate #30 and #31 in set23. 3. Bug setting listflags... Workaround: set listflags=(A /bin/ls) 2. Typo in Imakefile (# comment instead of c comment) 1. Typo in ma.setp.c (missing parenthesis) V6.06.00, 05/13/95 88. Cleanup off-by-one error ed.defns.c. 87. 6.05.09 - 05/06/95 86. Small memory leak in dosetenv() 85. Make sure that the number of editing functions defined is correct and abort otherwise. 84. Completion Fixes from Tom 83. Don't add yp stuff in the tilde cache [names that start with + or -] 82. Don't let children catclose() in xexit(), because the parent will lose access to the nls catalogs. From Michael. 81. 6.05.08 - 04/29/95 80. Update to the newest csh sh.file.c 79. More completions from Tom. 78. Fix the Imakefile to use XCOMM 77. Update for AIX 3.2. 76. French catalog from J.M.Vansteene@frcl.bull.fr (fwd Michael Schmidt) 75. Nls fixes and small typos from Michael 74. PDP11 BSD type fixes. 73. More manual page fixes. 72. 6.05.07 - 04/19/95 71. More NLS catalog fixes. 70. Bruce's jumbo patch. 69. 6.05.06 - 03/15/95 68. make clean does not clobber config.h if Makefile.std is used. make veryclean does. 67. New config define NISPLUS. Reportedly fixes vanishing output of ~expansion on solaris 2.4. 66. showdots has been removed; use listflags instead. [handles -x too] 65. more nls catalog fixes. 64. set histchars=,. did not have any effect in .cshrc 63. fix "-c command" new core dump. 62. more completions from Tom 61. call catclose() before exiting, so that svr4 cleans up the symlinks before exiting. 60. Per's fix for insert-last-word 59. Per's emacs abbreviation mode. 58. Makefile fixes for catalog stuff. 57. fixed again complete-word-raw and list-word-raw 56. 6.05.05 - 03/11/95 55. Made %?str work again 54. IRIX sigalarm problem should be fixed 53. complete-word-raw and list-word-raw should work 52. nls catalogs complete reworking. 51. sh.init.c: Signal complete rewrite. 50. Signal fixes from Bruce. 49. Added promptchars, like histchars; affect %# in the prompt. The first char is for the user and the second is for root. 48. 6.05.04 - 03/03/95 47. Add NODOT config, $command [that holds the command passed with -c] $GROUP and $group 46. AFS fixes from Larry Schwimmer 45. Real NLS catalogs from Michael Schmidt 44. patch to allow ^? binding. 43. completion patch from Paul DuBois. 42. Don't spell check in here docs. 41. Incorrect normalization of usec in sh.time.c 40. directory printing and option parsing fixes [from Paul DuBois] 39. realloc_searchlen should be static to avoid conflicts with OS's that use the same malloc package. 38. LOGINFIRST now applies to /etc/csh.login 37. Fixes in bind compatibility code to bindkey. 36. 6.05.03 - 01/17/95 35. Don't print the whole watchlist on startup. One can use explicitly the log builtin for that. 34. Don't display the $watch value in the log command. 33. Don't delete the unmatched portion if we try to complete a spelling error. 32. Solaris 2.4 workaround for isprint('\t') lossage (From Casper) 31. Fixed csh bug: [Reported by Jaap] set test="one\ two\ three" echo "$test:q" 30. Fixed the lossage in setting the looking mode in tw.parse.c before calling t_search; $x/$y will not append the right suffix. 29. Another attempt at the gethost timeout code. 28. Cray dmmode and filetests -m -k [need documentation!] 27. More manual patches. 26. Don't glob the path; security problem and core-dump. [from beto] 25. Avoid waiting for jobs that failed to restart, thus hanging the shell. eg, run job in background, attach to debugger; kill it inside the debugger. Get out of the debugger and you are stuck if you fg that job. 24. 6.05.02 - 09/04/94 23. remhost code could pass the wrong length to getpeername() 22. too eager sed'ing broke convex getwarpbyvalue() to getwarpbyvarval() 21. Fixed new bindings bug. 20. Fixed bug in newgrp code; execv was called incorrectly. 19. Take into account programmable completion hints when spell checking. 18. Unconditionally change stty modes in Rawmode() 17. Change date in $version to be ISO 8601 per Keith's suggestion 16. Fixed portability problems in the new counted-strings codes (From Kaveh) 15. Fixed binding problem with the new counted-strings code [all key bindings ended up in the extended map] 14. 6.05.01 - 07/08/94 13. Changed key bindings to use counted strings instead of Null terminated strings, so that binding ^@ works. 12. Fix parsing bug, where words that contained keywords caused incorrect word breaks: switch (iftagd) case iftagd: echo It works breaksw default: echo It is broken. endsw 11. Fix sh.c for systems with no job control. [the new hup builtin sends SIGCONT]. 10. Don't add the suffix character when all completions fail, from michael 9. Added histdup=erase, that deletes the oldest duplicate instead of the newest. 8. Don't call .logout recursively if we receive more than one hup, and don't process hangups when we execute the .logout. 7. $logout was set to a static string. 6. Non printable binding listing was broken. 5. Recursive sourced scripts would get mangled up because of input buffer confusion (thanks michael) 4. M-space should not be counted as a space. Leads to infinite loop in word splitting. 3. Pyramid fixes. 2. Fixed '\' in sysv echo (from Mika) 1. Missed fix_yp_bugs() when setting REMOTEHOST (From Casper) V6.05.00, 06/19/94 113. 6.04.17 06/18/94 112. Realloc debug fix from John. 111. Completion fixes from Bruce. 110. Added -b option to bind. 109. Added tcsh.man2html from Dave. Looks great! 108. 6.04.16 06/06/94 107. Allow multi word glob in "`foo`" expressions. > if ("`ls`" == "a b c") then 106. Michaels lex() buffer patch version 2. the previous version core-dumped with > sleep | echo 105. Fix seg fault in find_cmd, when path is unset. 104. 6.04.15 05/28/94 103. the previous =~ fix, was a bit broken. 102. 6.04.14 05/25/94 101. expression parser fix for !~ and =~. if (foo =~ *) echo ok the * was taken for a multiply and parsed as 0 * 0 returning 0. and asked to match "foo" with "0" 100. Michael's lex() buffer patch. 99. Bruce's Jumbo cleanup 98. fixed Imakefile for X11R6 97. fixed argument parsing in source command. 96. added $sched to format scheduled events. 95. 6.04.13 05/07/94 94. added expand-command 93. Alpha OSF-2.0 fixes 92. 6.04.12 04/28/94 91. Ignoreeof patch from Bruce. 90. Dan's completion fix. 89. 6.04.11 04/12/94 88. Don't source .logout when killed with -HUP unless we are a login shell. [ actually $loginsh controls the behavior now ] 87. ISC-4.0 posix exec() bug workaround (from Jonathan Broome ) 86. Dan's patch for complete=enhance 85. Kaveh's patches for memset, host.defs, _{U,G}ID_T 84. 6.04.10 03/29/94 83. Pathname completion bug fixes... 82. Completion pathname(p) changes: Completion 'C' -> 'X'. Replaced 'p' with 'F' and added D,T,C. 81. Next NLS fixes (From Paul Nevai ) 80. make spelling correction obey the current histchars setting. 79. Fix rounding in limits so that 0 is a true 0. (From Ken Lalonde ) 78. 6.04.09 03/12/94 77. Alarm race with remotehost. 76. If you have in your .cshrc set savehist=(100 merge) and execute #!./tcsh exec ps you get no output; [fixed] 75. setenv SHLVL -1; tcsh = Segmentation fault [fixed] 74. 6.04.08 02/10/94 73. new machtype, ostype, vendor 72. execute .logout on hangup. 71. more man page fixes. 70. 6.04.07 02/04/94 69. Dynamic hash changes, globbing for which command from Michael. 68. INBUFSIZE fixes for history. 67. printenv returns 1 when a variable is not found, like the non builtin one 66. Add quotes to the mismatch array so that: > touch foo > mail "oo bar" does not get corrected. The right fix would be to do correct parsing and quote the space... 65. NULL terminate mismatch[] array in ed.inputl.c, since it is used in Strchr(). 64. renamed stat to filetest 63. Afs and Hesiod patches. 62. More manual page patches. 61. 6.04.06 12/19/93 60. Alarm for resolver timeouts (untested) 59. Fixes for Henry Spenser's regex in the Makefiles 58. Modifiers now work with $< and $< does not do :q by default. [incompatibility with csh] and $< can be interrupted. 57. Added $histdup. 56. source /etc/.login for Solaris 2.x 55. don't remove trailing spaces in history searches. 54. dirs -L and dirs -S spurious warning elimination. 53. man page fixes from David. 52. Kimmo's patch [makefile, gcc warning, netdb.h include] 51. 6.04.05 12/12/93 50. rewrote srcunit() to use st_save and st_restore. The code was too messy to be useful before. 49. recdirs and rechist patches again! 48. Harris hck port, and ISC imake update 47. Eliminate the x windows :display.screen before looking for the hostname in REMOTEHOST 46. Patch for CDF filetype() recognition (hpux) 45. 6.04.04 11/12/93 44. Made =x obey nonomatch. 43. Fixed rechist and recdirs not to depend on savehist and savedirs 42. Removed aliases, bind, linedit builtins [ifdef'ed out as OBSOLETE] 41. change REMHOST to REMOTEHOST for compatibility with SGI. 40. $owd was not working correctly. 39. Debugging printf elimination (from Kimmo) 38. Typos in the new hup code in sh.c (from Matt) 37. Imakefile fix for sequent (from Jaap) 36. 6.04.03, 10/30/93 35. New manual page in place. 34. Fixed Makefile sed RE to work with gnu sed. 33. Added hup builtin. 32. Added $REMHOST in config_f.h 31. Fixed random parsing bug in sh.exp.c in isa(). It checked cp[1] without checking cp[0] first... (should propagate to 4.4BSD) 30. More sh.exp.c fixes from Bruce 29. Always exit when the user types exit. cat | tcsh -f -s exit ls 28. savedirs fix and readlink() null termination fix from Peter. 27. Added -p flag (force dirs printing in all directory functions and $owd variable 26. Let $dirstack work by default without needing to be set. 25. Fixed history -h to work with other history flags. 24. Renamed ed-functions more consistently (dcs) 23. Added missing filetests (dcs) 22. Added NEWGRP define in config_f.h (dcs) 21. More fixes for non blocking io recovery from Bruce 20. 6.04.02, 10/08/93 19. changed default prompt to %# 18. New group 'g' completion. 17. removed IIASA defs. 16. fix for history -SL and dirs -SL to work when savedirs and savehist are not set. 15. fix magic-space to understand :p 14. make :u,:l work for history. 13. Fixes for non-blocking i/o recovery. trap all non blocking styles under sunos. 12. 6.04.01, 08/10/93 11. Vi mode word moves [they work like vi and don't obey wordchars anymore]. 10. New file operators and stat builtin 9. David's FIONREAD patch for SVR4 8. Bruce's ignoreeof=n patch 7. Dan's complete/correct enhancement 6. Abort corrections patch. 5. Print_by_columns, now does not use the rightmost column. 4. Renamed config/config.$i to $i to avoid once and for all filename length problems. 3. bbn butterfly config 2. Beto's proc fixes 1. Bruce's bug fix for ed.chared.c, signals V6.04.00, 07/03/93 92. Alpha prototypes, for gcc -Wall clean compile. 91. Fixed alpha hashing. Did not work for directories > 31, Thanks to Dan Mosedale for all the help! 90. Added USE(a) for unused parameters, to avoid compiler warnings. 89. 6.03.10, 06/25/93 88. Makefile and lint patches from Bruce. 87. Posixsig patch from Peter for svr4 86. 6.03.09, 06/11/93 85. signal handling broke with the last job control patch... > (does-not-compute;) would hang. 84. don't quit after the first modifier fails: > set i=aabbcc > echo $i:s/a/A/:s/b/B/ AaBbcc > echo $i:s/x/A/:s/b/B/ aabbcc 83. Imakefile fixes for sequents from Jaap. 82. Move past the bottom of input when we hit ^C, so that multi-line input commands don't get trashed. 81. Look only at the first word in ${EDITOR,VISUAL} to determine the editor name. 80. Last patch broke listmax. 79. Remove extra sandbagging in exitstat(). Not needed anymore as value() will never return NULL. 78. Save $status before calling aliasrun() otherwise: > alias precmd 'cd .' > true > echo $status 0 > false > echo $status 0 77. 6.03.08, 06/07/93 76. missing prototypes/unused variables under SYSV4. 75. savedirs should glob the filename argument. 74. 6.03.07, 06/05/93 73. Changed _Q, _Q1 macros in sh.lex.c, sh.dol.c, sh.char.? to _QF and _QB because today 4.4BSD defined _Q to be ``Phonetics'' 72. listmaxrows variable. 71. set -r would not make existing variables readonly. 70. print usernames with ~user where possible in dirs and job cwd's 69. Mika's fixes (missing args in sh.c set(), linux Makefile, SIGCHLD) 68. FIONREAD argument is int everywhere except on SunOS where it is long. Tcsh used to think that it was long, and so it broke on the alpha where sizeof(int) != sizeof(long) 67. 6.03.06 05/16/93 66. Pipeline fixes. This should get rid of the dreaded Stopped tty output message. 65. Michael's fix for history buffer cleanup: > alias foo 'echo \!:1:h' > foo a/b a > ^P [nothing happens] 64. Unsigned long rlimit type for 64 bit machines (alpha) 63. Fixed Imakefile for alpha 62. tilde expansion now obeys $nonomatch 61. readonly veriables. 'set -r x=3' will set x to a very sticky 3... 60. Fixed bug in the new tab'ed completion that interfered with old listing. 59. Fixed entry -> item in tw.parse.c 58. Added -f option to unlimit 57. Define __STDC__ in Imakefile for Irix 56. Static redeclarations in tw.parse.c 55. 6.03.05 04/26/93 54. Don't echo history expansion in loops: > alias junk 'foreach i (\!:2*) \ foreach? echo \!:1 $i; \ foreach? end' > junk a b c d echo a $i ; a b echo a $i ; a c echo a $i ; a d 53. Added complete-word-fwd and complete-word-back that scroll through the list of possible completions... Neat addition from jfink@csugrad.cs.vt.edu (Jeff "$DOTDIR" Fink) 52. Workaround hpux9.01 broken optimizer in sh.glob.c where 'ls *' breaks. 51. Michael's editor fix. With "magic margins" and 80 char width, try: > set prompt= 20aa76bac 50. Reverted fix that does not glob the expanded back-quote text again. 49. > cd /., echo $cwd /. Eliminate the . in this case... 48. Matt's optimization in glob.c [retracted] 47. Fixed bug with -S 46. Renamed Makefile Makefile.std, and fixed the readme file 45. 6.03.04 04/07/93 44. Changed -s == !-z and -S == socket 43. Fixed typo in sh.c, in phup [shpgrp != pr->pgrp] 42. Multi-line aliases are not entered into the history when executed. 41. Changed f_seek to a union to avoid gcc warnings on 4.4BSD 40. Fixed 'unset home; dirs' bug 39. fixed $HOME->$home memory problem. 38. $?0 returns false now on interactive shells for csh compatibility. 37. Default watch inteval was 10 hours not 10 minutes as advertized in the manual! 36. Fixed clobbered veriable warning from gcc-1.39 in tw.parse.c 35. Fixed typo with INVPTR in sh.lex.c 34. 6.03.03 03/04/93 33. Eliminate 'Reset tty pgrp error message'. It is normal for the walking process group stuff. Check about the killpg() above? 32. More portability for 64 bit machines. 31. Improved completion error messages. 30. Fix bug with double globbing expansion on mirrored variables: > setenv TERM '?vt100' 29. Avoid hanging when writing to pipes etc. (From Paul Close) 28. Typo in the $HOME->$home mirroring. 27. %p %P 'p'recise time formats that include seconds for prompt strings. 26. Signal list fixes from Paul Close. nsig is now a variable. 25. 6.03.02 02/12/93 24. NEC SX3 "entry" is a reserved word!, changed entry to item... [this is as bad as cray typedefining "word" to int. Next thing I know, I'll have to prefix all the variables with tcsh_!] 23. xxx 22. Protected all include files to avoid warnings from lcc. 21. Finally resolved the job control problems with linux and ISC (from Beto) 20. More HUP,XFSZ,XCPU fixes. (with Beto) 19. Dcanon would not canonicalize correctly in case where dnormalize() failed to find a directory, but the directory existed when not crossing the symbolic link. 18. sh.init.c fixes; give 65 signals for all POSIX machines. 17. AMS mail fixes and cmu wm handling. 16. Setenv would not ignore trim quoted names... > alias foo 'setenv "\!:1" bar' > setenv FOO 123 > foo FOO > printenv ... FOO=123 FOO=bar ... 15. 6.03.01 08/01/93 14. #undef SHORT_STRINGS gcc warning fixes... 13. csh bug fix in foreach [quoteing problem] > foreach i ("*") > echo $i:q > end Should print * 12. ls-F obeys showdots... 11. After scheduled commands get executed, the editor could stay disabled until the first enter. 10. sigset() bug in ed.init.c; ^C could get disabled sometimes. 9. Don't glob in dowhich(); otherwise \pat does not work if pat is aliased. 8. When completion patterns are used $ and ~ expansion did not work right. 7. Printenv builtin and long backquote expansion fix from Harald. 6. $:-1, $:-0 enhancement from Paul and Michael. 5. Lynx/OS patches 4. Apollo bugs [Setenv -> tsetenv, typedef in pid_t] 3. ^@ did not work correctly in bindkey. 2. Fix vms/posix ifdefs in tc.alloc.c 1. Elide gcc-2.3.1 warnings V6.03.00, 11/20/92 102. Solaris2 patches, renamed sunosX to sosX to fit in 14 character filenames. 101. 6.02.12 11/12/92 100. Decreased BUFSIZE to 1K... 99. Don't use sbrk() in tc.alloc.c on VMS 98. EINTR error handling from Michael 97. Overwrite mode takes effect immediately, not delayed by a command. 96. Emx fixes. 95. SIGHUP gain... 94. Coherent times()/ fixes. 93. 6.02.11 10/27/92 92. Mach setpath fixes. 91. Which now globs its arguments 90. Again POSIX SIGHUP fixes... Maybe we got it right this time... 89. Increased BUFSIZE to 4K. Smaller machines may want to decrease this. [ Now one can edit up to 4K of buffer! ]. From Harald. 88. : is now a true builtin that does nothing, instead of being treated like the last character of a label. This is so it can take args. From Harald. 87. More fixes for hpux limits from Harald. 86. Login shell if ppid == 1 and argc == 1 for VMS. 85. Typo in sh.exec.c from Harry. 84. Per's editor fixes. 83. 6.02.10 10/17/92 82. Don't free STRNULL in freelex(). Update linked list in copylex() to do the same insert as in lex(). 81. Fixed spurious tilde printing in %c prompt. 80. Fixed dumb wild free in AddXKey... (thanks Harald for the complex bindings! :-) > bindkey "\e10z" expand-line # Any editor command will do > bindkey -[cs] "\e10z" "foo" # Any string or command will do Free(%x) .... 79. coherent additions. 78. fixes for hpux8 resource limits [Thanks Harald] 77. 6.02.09 10/10/92 76. More vms cleanups. 75. Removed #include CONFIGH from sh.h. There was no way to make it work under VMS/POSIX 74. Added t completion (Gray Watson) 73. Memory leaks in sh.dir.c and sh.glob.c [thanks purify :-)] 72. IRIX 5.0 patches (from Paul Close) 71. BSD resource limits for hpux8.0 (From Andreas Stolcke) 70. NGROUPS_MAX fixes (from Matt Day) 69. 6.02.08 10/04/92 68. VMS port additions. 67. Added $%var, which == strlen(var) 66. Added ^X^I and ^X^D to override programmable listing+completion 65. Added case insensitive globbing for OS/2 64. FIOCLEX and FIONCLEX are only used if we don't have FD_CLOEXEC. 63. Added emx termio support, and emx pathsep support; emx compiles and runs: needs job-debugging and finishing touches. 62. Horrible bug with the lex linked list; from Loic Grenie, grenie@ens.fr > echo a !# echoed 'echo a echo a echo'! 61. Shell -w,-x were incompatible with the same for test [POSIX] (beto) 60. builtin newgrp did not accept the - argument. 59. FIOCLEX for the masses (POSIX FD_CLOEXEC addition) 58. Old style csh completion bug fix [not used] (beto) 57. A background process waiting for input from tty blocks when being foregrounded doesn't read its input and doesn't get and signals. It can only be killed from another session. (beto) > cat > foo #!/bin/csh -f echo ">>>>>>" set x = $< echo $x ^D > chmod +x foo > foo & > fg ^Z,^C # Nothing happens 56. history > 1000 fix (beto) 55. 6.02.07 9/18/92 54. Added e_stuff_char() to put back a character in cooked mode in the input stream... Useful for status (^T) 53. $? == $status and $# == $#argv like in the bourne shell. 52. Added $dirstack. Problems: off by one since =0 == $cwd, but $dirstack[1] == $cwd... Suggestions welcome. 51. Leading spaces would confuse the run-help function. 50. Don't expand imported environment variables: > setenv HOME \* > tcsh > echo "$home" > echo "$HOME" 49. %C0n displays the number of directories omitted as /usr/local/bin /<2>bin/ 48. Added POSIXSIGS as a configuration option. 47. Per's fixes for history format and arrow key binding. 46. Expand-variables expands array variables too 45. 6.02.06 8/14/92 44. Separated limit stuff from BSDTIMES and added BSDLIMIT 43. History searching inconsistent with HistLit: > echo foo foo > ^foo^bar bar > ec[M-p] echo foo 42. 6.02.05 8/8/92 41. sourcing a script that contains 'onintr -; onintr' would disable interrupts on the command line 40. cd ../... with symlinks=ignore would work !@#!@ 39. No more BACKPIPE for POSIX machines... 38. SUNOS NLS bug workarounds... 37. New HASHBANG define for #! emulation... even 386BSD does not have it yet... 36. Added new -b -c file operators from SGI csh. 35. 6.02.04 7/23/92 34. Walking process group fixes. 33. $0 contains now argv[0] when we are not executing a shell script. 32. New Getwd() was broken. 31. 6.02.03 7/17/92 30. We kill our last foreground process group on HUP, for POSIX systems. 29. Walking process group fix. 28. $shlvl gets reset to 1 for login shells. 27. Macros can now involve multiple commands, i.e. bindkey -s "^[OP" "ls\n^P" 26. 6.02.02 7/6/92 25. nostat accepts now a list shell patterns. 24. Avoid print_exit_value in if ( { foo } ) 23. Symbolic key names for bindkey [arrow keys]. 22. Smarter getwd() that avoids some of the NFS hangs... 21. Use memmove() where possible. 20. 6.02.01 6/16/92 19. Fix automatic correction to work with the new completion. 18. Globbing bug with brace expansion, when arguments need to be realloced... e.g. echo 134{6,7}{0,1,2,3,4,5,6,7,8,9}{0,1,2,3,4,5,6,7,8,9} 17. shift and left operators update variables correcly ('shift path' did not work) 16. apollo fixes for cd. 15. STRNULL <-> NULL bug fixes. 14. %S in xprintf for short strings. 13. history -LSc, dirs -LSc additions, builtins builtin [oxymoron] 12. history formatting string, who formatting string, prompt formatting all use tprintf() now. 11. $history accepts a second formatting argument.. 10. History timestamps preserved in .history 9. Backquotes were broken: echo hi | cat > `echo foo` 8. SGI -t operator problem. 7. Dnix fixes. 6. Fix history file writing bug when autologout. 5. Change from TCSETA to TCSETAW, since we affect the output. 4. Per's fixes for display margin bugs. 3. Linux fixes. 2. Collapse adjacent stars in glob.c to avoid exponential behavior. 1. Fixed parseescape to put ^ literally if not followed by alpha. V6.02.00, 5/15/92 94. Default echo_style was not being set correctly. 93. $< did not work correctly with NLS chars. 92. 6.01.15 (5/11/92) 91. Minor cleanups in refresh code. 90. 6.01.14 (5/8/92) 89. Print \n in the right margin always to be consistent. 88. Linux patches. 87. 6.01.13 (5/2/92) 86. Fixes in listing and completion of non-unique commands. 85. Fix =stack entries > 9... from Bill Petro 84. Fix for last character at margin. [on terminals that have am & xm move the cursor manually to the next line] 83. Fix for complete completions 82. small typo in tc.alloc.c from Mark Moraes. 81. small typo in tw.parse.c from Richard. 80. Man page fixes from Per. 79. 6.01.12 (4/24/92) [release version for 6.02] 78. Autosuffix was not getting reset all the time. 77. Don't limit completion patterns to 1K. 76. Naming changes in ed.screen.c from Justin. 75. 6.01.11 (4/10/92) 74. Fixed magic-space ! expand bug: > echo a b123 45.{6,7} > echo !:2!:3[magic-space] 73. Work-around for apollo optimizer in tc.bind.c 72. Problem with setjmp in tw.parse.c 71. symlinks=expand from Yumin. 70. tenematch() fixes from Michael; 69. 6.01.10 (4/3/92) 68. Fixed Gnmatch. 67. Removed ignore_symlinks and chase_symlinks. Replaced by symlinks variable. 66. Backquote from completion was broken for machines with no dup2() 65. No $printexitvalue for `` jobs. 64. Added ultrix stuff for PW_AUTH [from Mike Potter mpotter@lampf.lanl.gov] 63. Fixed bug in Gnmatch(), added 'N' command 'x' completion. 62. Fixed fg_proc_entry() so autologout gets disabled when a process is foregrounded. 61. 6.01.09 (3/26/92) 60. source builtin takes arguments now that are passed in the script e.g.: > ./tcsh -i a b c > cat test echo $argv > echo $argv a b c > source test 1 2 3 1 2 3 > echo $argv a b c 59. Eliminate $margin_bug, add more intelligent margin code [from Justin] 58. expand_symlinks variable Urgh... 57. SGI CONTROL() fixes... 56. new dnormalize(), expand_symlinks flag. 55. interrupting completion restores the current line. [from Marc] 54. 6.01.08 (3/20/92) 53. inputmode affects the first line too. 52. Searches in vi do not destroy the current line. 51. -drwX test enhancement. 50. Generalized completion syntax and added more completion builtins. 49. Don't print '\n' on the right margin if we can avoid it. 48. 6.01.07 (3/9/92) 47. New completion style. [[[ ]],=] 46. 6.01.06 (3/7/92) 45. complete styles are ignored after shell metachars eg. > complete mail =u > mail christos < [tab] completes filenames. 44. $echo_style is initialized to the default style of the machine. 43. SGI 4.0 CTRL() macro is busted. So we ignore it. 42. Fixed bug with the new margin code (thanks justin) 41. Fixed mirroring of variables. Now set and setenv affect HOME, TERM, PATH, SHLVL, USER and their csh counterparts. Unset affects affects only the csh copies of the variables, and unsetenv only the environment variables. 40. Fixed gcc-2.0 warnings in sh.sem.c (needed for vfork() -O2 optimization) 39. 6.01.05 (2/21/92) 38. signal masking for BSD machines was still wrong in tw.init.c 37. Added $margin_bug, and now the editor outputs on the rightmost column by default. This should fix the xterm cut-n-paste problem. 36. AU/X 2.0 and SGI compiles with POSIX as a default now. 35. sysv_echo is gone too. Now we have a variable called echo_style which can be set to "none", "bsd", "sysv", "both" that defines the builtin echo_style. The default is "bsd" for systems with SYSVREL == 0, "sysv" otherwise. This can be overriden in config.h by defining ECHO_STYLE ro be BSD_ECHO, SYSV_ECHO, BOTH_ECHO, or NONE_ECHO. 34. asynchronous notification in run-fg-editor would try to change the tty settings and print an extra prompt. > set notify > sleep 10 & > vi^Z > ^[^Z 33. alias x /bin/true x? reported .//bin/true instead of /bin/true 32. 6.01.04 (2/12/92) 31. fixed vi_substitute_line 30. binding completion addition. 29. showdots=-A addition 28. sysv_echo addition; deleted bsd_echo which is now the default. 27. dunique could crash. 26. Don't clear lines after an asynchronous notification, because it might erase the message. 25. 6.01.03 (1/29/92) 24. `` were not working correctly when tcsh is started with 0,1,2 closed. 23. Correction was picking up the wrong thing in cases like .rhosts hosts 22. Correction with names with quoted spaces was broken. 21. Added sysv_echo and bsd_echo. 20. Added and documented the complete builtin. 19. Added dunique and cd -. 18. Per's fix for alarm functions. Things would break if time went backwards. 17. Realloc bug fix (copy min of old and new size) 16. Fix for csh compatible braces. 15. Brace globbing and negation globbing for Gmatch. 14. 6.01.02 (1/16/92) 13. Completion builtin, new tw* files. 12. 6.01.01 (1/6/92) 11. Changed w_ fields in tc.who.c to who_ to avoid redefinitions in in some os's. 10. Setting savehist to an empty string makes savehist use the value in $history. 9. echotc did not use the internal termcap descriptions. 8. echotc did not work right for single argument termcap strings. 7. DGUX needs SAVESIGVEC, updated type protection, and disabled CSWTCH. 6. Changed SVID to SYSVREL, since there is no SVID == 4 (yet). 5. Typo in tc.sig.h: UNRELSIGS was not getting defined. 4. Globbing bug fix. tglob() should ignore globbing characters inside backquotes otherwise things get expanded twice: % echo 'bar?' > 'foo?' % echo `cat foo\?` echo: No match. 3. Mach setpath fix. 2. Alliant process group fix. 1. Xenix fixes for broken vi and help path with no suffixes. V6.01.00, 12/19/91 [for comp.sources.unix] 22. Restore the original prompt in automatic correction when the user asks to re-edit the command. 21. Better error for unreadable files in tw.parse.c 20. Sequent patches. 19. (V6.00.08 beta 12/14/91) 18. Filenames containing # were spelled/expanded incorrectly. 17. Stellar/stellix port. 16. Hpux 8.0 has SIGWINDOW but we cannot get the window size. Protect against window changes updating $LINES and $COLUMNS. 15. Mach setpath did not work right. 14. Mach does not need setenv in lib; in fact that does not work right. 13. shell level gets decremented when we exec. 12. restart_fg_editor picked the wrong editor if arg contained a slash. e.g vi ~/.cshrc 11. (V6.00.07 beta 12/08/91) 10. Allow use of vfork() on systems that do not have job control. 9. Avoid NUMCC from being defined twice. 8. (V6.00.06 beta 12/05/91) 7. SunOS3's FIOCLEX dups... 6. Yet another globbing bug fix from Michael (echo .[^.]) 5. Ultrix now wants __ before cpp symbols. 4. Workaround sun's header files inconsistency so tcsh can be compiled with #undef POSIX 3. Fixed limit stuff so that large values don't cause errors. 2. Fixed =1 and ~user error messages (Per) 1. (beta-5 6.00.04 == 6.00.05) $#* $#* New numbering scheme (The last 2 digits are reserved for beta releases $#* from now on. So we start at 6.00.06 which is 6.00.04 beta 6, and we are $#* going to release 6.01.00... $#* V6.00.04, (never released) 34. (beta-5 11/25/91) 33. Simplify code in BindArrowKeys() 32. Get rid of the PNULL's 31. Support for shadow password in locking code. (From Kimmo) 30. Normalize-path editor function. 29. (beta-4 11/21/91) 28. minix additions 27. bindkey fixes (-c addition, casting cleanups) 26. (beta-3 11/16/91) 25. Prompt length checking. 24. Michael's fixes for the watching code. Tcsh could core dump when using a corrupted utmp. 23. overwrite mode flag, and autologout locking code. 22. overwrite mode would still insert digits! 21. Get the value of _POSIX_VDISABLE from pathconf... 20. Hpux 8.0 fixes. Sigstack botches with shared libraries. Ansi mode static initialization of structures with prototyped function pointers gives spurious warnings... 19. Onintr in /etc rc files is disabled. 18. (beta-2 11/03/91) 17. Multi-line aliases with `` bug fix. 16. Sticky emacs overwrite mode. 15. Autolist option. 14. Fix for redirection with wildcard filanames. 13. No error correction for multi-line commands. 12. Esix-4 re-defines p_pid. Argh... 11. (beta-1 10/28/91) 10. Foreach loops were broken again. 9. SUNOS localtime() bug does not only happen on the 8th byte. [Michael] 8. sh.dir.c, memory corruption in dinit(). Thanks Michael. 7. uts broken work-around, and has a wait3() 6. SGI (-t filename) extension, returns true when filename is a tty. 5. 'default:' addition in all switch() statements. 4. oflag was not being updated correctly in Rawmode() 3. missing 'break;' in prompt code '%y'. 2. infinite loop in :a code. 1. hashstat was not reporting hits+misses V6.00.03, 10/21/91 31. watch code now accepts shell patterns. 30. new hashing code portability fixes. 29. $foo:gs/:/ / fix and 'a' modifier addition. 28. Added $! (the pid of the last background job forked) 27. exec does not kill the shell when it fails. 26. Irix4.0 decls. 25. SVR4 uthost fixes from Kimmo Suominen 24. Imake addition from marc 23. New i-search from Per. 22. ibmesa fixes 21. convex fixes. 20. t command for vi. 19. SVR4 fixes (reverse pipeline and sigaction()) from David Dawes. 18. New hashing from Marc 17. Added : to the ~name separators so ~foo:~bar works. 16. New ed.init.c. Added ed.term.c 15. Still can't get the history exactly csh-like... !-2$ was broken... 14. vi character searches. 13. -Dvar=name command line option for the apollos. 12. Prompt format changes for date/directory. 11. Vi searches. 10. Emacs i-search. [currently not bound] 9. Vi additions ([dc]-{w,$,f},Undo) 8. tcsh -n parses now builtin structures. 7. seek to the end on errors in loops. 6. echotc -s was broken 5. Better !event parsing. !foo;!bar was broken. 4. foreach loops and if statements in aliases. 3. .cshdirs would corrupt the heap if some directories were not there. 2. System V echo was broken with \ 1. Fixed echo '!-1', history would eat the quote. V6.00.02, 08/05/91 [For comp.sources.unix] 8. tcsh will always set $LOGNAME and $USER if not already set in the environment. 7. added $histfile. 6. echo `echo` * and echo ~ {} were broken 5. setty builtin addition 4. Multiple : modifiers [experimental, disabled with -DCOMPAT] 3. 7 bit fixes, and hp9000s500. 2. #undef DEBUG in tc.alloc.c, so we continue if we get a bad free() 1. getn() is now protected against NULL strings. V6.00.01, 07/16/91 17. added beepcmd, and fixed small tenematch bugs. 16. Renamed DUP2 flag to HAVEDUP2 cause AIXPS2 defines DUP2 already. 15. More ANSI fixes, and mit additions (load-average) 14. don't clear ECHOE, background programs might need it. 13. Refresh bug fix... 12. getpwent() should not be interrupted (yellow pages), cause it might leave dangling pointers, that endpwent() will trash. 11. ChangeSize is now responsible to set the screen size to something sane, but it should not affect the environment if the information it received is not valid 10. Find out if we are running under emacs using $TERM. It is more reliable. 9. tcsh was broken if SHORT_STRINGS was not defined (glob() problem) 8. If GetSize() fails it should always return reasonable screen sizes. 7. globbing should not fail if one or more patterns match. 6. increased the number of aliases in the loop detection code. 5. DGUX has size_t and pid_t defined now... 4. ESIX does not have EWOULDBLOCK or EAGAIN & POSIX... That was not handled correctly. 3. rs6000 needs BSDWAIT. 2. Hpux susp key could not be changed. 1. Apollo fixes. V6.00.00, 07/04/91 45. Fixed quoting of VSTART/VSTOP on termios 44. Memory leak every time you pipe in or out. 43. echo {foo bar.[ch]}. Did not check for end of word. 42. ANSI prototypes 41. dmove() would leave the source descriptor open when using dup2() e.g. while (1) echo foo >>! bar end runs out of file descriptors 40. recursive `` expanded via aliases would abort. 39. set home=../relative-path-name was broken. e.g. set home=../..; cd ..; 38. Incorrect error when changing to directory: e.g. mkdir not-in-cd-path; chmod -x not-in-cd-path; cd not-in-cd-path; echoed: not-in-cd-path: No such file or directory. it should have said not-in-cd-path: Permission denied. 37. if ( \! =~ [\!] ) echo ok, did not echo ok. Quoting inside [] pattern. 36. More than 127 jobs caused job # to go negative 35. unsetenv ... Added support for more than one pattern. 34. More background process status report fixes. 33. Apollo builtin support. 32. Glob fix. echo '*' * was not handled properly. 31. Glob fix. setenv FOO `sleep 1` would either cause a segmentation fault. or print ambiguous. 30. Glob fix. Quoted characters inside [] were not handled properly. 29. Removed TELL and VMUNIX defines. I think we cannot compile on V6 anyway 28. Overflow check for expansions. 27. Added matchbeep, shell pattern history searching. 26. Added code to handle /dev/std{in,out,err} 25. Fixed POSIX speed handling in raw mode. 24. Fixed a flushing bug in the filec code in sh.file.c 23. Compiled and added hp9000s700 to the list of hosts. 22. Fixed horrible bug in gmatch() switch (4) case [a-z]: echo bug; breaksw; case 4: echo ok; breaksw; endsw 21. Fixes for _SEQUENT_ ut_host. 20. FLUSHO added 19. mkdir foo\`bar; cd foo\`bar; was broken 18. EWOULDBLOCK == EAGAIN on RENO check for that too. 17. Dword() simplified and removed the gotos. 16. Hpux now compiles with POSIX. Moved the local chars modes in ed.init.c after the tty modes. 15. Process group fixes for POSIX 14. We need on the suns for FIOCLEX!!! We did not close our file descriptors before... 13. A/UX fixes. 12. History events that start with a number are not necessarily numeric: > !3d 3d: Event not fount 11. History loop detection added. > alias a \!# > b; a 10. Alias loop detection code was ineffective on eager optimizers. 9. All errors should now go through our table. Next step we should add nls error messages. 8. Unsetenv now globs its arguments! Before it did not... 7. Added tilde cache and -l flag. 6. Added autocorrect 5. Fixed for so that background jobs in scripts get process groups 4. Fixed amazing memory leak in setenv()... 3. Added short2qstr() so that we can form quoted strings to be used with glob() 2. str2short and short2str allocate space dynamically. 1. Fixed bug related to the is*() routines called with shorts. [only if NLS and SHORT_STRINGS are defined]. (The _ctype_ was getting indexed with shorts...) 0. Complete overhaul. Brought in the 4.4 csh stuff. Separated most tcsh and csh code except where I would have to add more global variables. Compiled correctly with both lint and gcc -Wall on a sparc running 4.1 V5.20.03, 03/20/91. - Never released 25. Kanji, SXA additions. 24. (exec foo) should not fail if they are suspended jobs. 23. Support kernel paging stuff on aix 370. 22. Now we handle correctly the environment variables LINES, COLUMNS, TERMCAP for window size changes... 21. Tcsh 5.18c+ had broken NULL chars on scripts. > cat > foo #!/usr/local/bin/tcsh -f echo "foo" echo "^V^@" echo "bar" ^D > chmod +x foo; foo foo 20. Posix has EAGAIN and not EWOULDBLOCK 19. Don't set ECHOK; makes kill ^U ugly. 18. $shell is SHELLPATH and not $SHELL any more. 17. Added -n flag to cd, pushd, popd and dirs. Documented -l and -v flags. 16. Documented and fixed chase_symlinks and ignore_symlinks. 15. Call endpwent() when you get interrupted... cd ~chri^C cd ~christos/ 14. Exec when you have suspended jobs asks for verification. 13. Variable length fixes: >set abcdefghijklmnopqrstuvwxyz=1 >echo $abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrs: Undefined variable Now we have better messages too... 12. Icon fixes... 11. Quote expanded glob and history chars too. 10. If someone sets SHIN to O_NDELAY we died... Now we set it back... 9. unset path; unsetenv PATH; ls-F; (poof) fixed... 8. Patches for masscomp, and ${var123} lexical analysis fix... 7. Patch for sunview bug: A partially covered suntool sends SIGWINCH every time the text is scrolled, causing annoying redraw effects. Now tcsh checks if the size really changed before doing anything. 6. Interrupt in the middle of cwdcmd, and periodic does not remove the aliases. 5. prompt2 and prompt3 fixes: Prompt2 now prints the status of the parser by default. Prompt3 can now have the standard prompt escape sequences. 4. eval file descriptor re-direction fix. >set p='w | tail +3' >eval $p (originally | tail +3 was ignored) 3. $edit != emacs anymore; it was misleading, 'set edit' enables editing 'unset edit' disables it. 2. PERROR define fix. 1. irix3.3.1 line discipline fix. V5.20.02, 12/15/90. 32. /etc/Logout added and merged with /etc/Login flag. 31. Fixed tty chars in ed.init.c. Some of them were not handled correctly. 30. Command execution does not leak memory any more, and doing ~ four times does not core-dump. 29. Fixed the notorious csh 'if(' bug (part of the convex fixes) 28. Intelligent getwd() on startup, and canonicalization. 27. Intelligent directory stack. Gets updated when $HOME changes. 26. Directory stack/save-restore additions 25. Convex Fixes. 24. Fixed onlret, and echonl getting stuck on svid. 23. Shouldn't leave tty in raw mode at auto-logout (e.g. when su'ing from a csh, this is annoying). 22. Some users had environment EMACS set to something, and with my bad fix at pl 1 found that they didn't have echo... (of course, even before my "fix", they didn't have editing...). Anyway, refined the check to be for EMACS=t, which is what emacs sets when running a subshell. 21. kill -CONT %job, would not update the status of the job, but it would just restart it. 20. Sequent fixes. 19. AIX370 signal and TCF fixes. 18. fixed ls-F -l, SIGINT problem. 17. aix on the ps2 does not have strcoll() either. 16. Bind [A - [D and OA to OD in vi mode to the arrow key functions so that they work from insert mode. 15. %~ was not working correctly for /home/news and /home/newsbin... 14. Removed Rcs Log 13. ISC unix fixes. 12. sunos3.x wants not 11. Remove precmd's, cwdcmd's and periodic cmds that had errors. 10. Compile sunos4.1 using termio. 9. Rs6000 line discipline fixes. 8. T_Tabs was wrong for termio machines. 7. Don't bind single keystroke arrow keys, when they are already bound! 6. Test for newline and tab before deciding not to send shell scripts to the bourne shell. 5. Added LITERAL prompt stuff. 4. Fixed gethostname() in sh.rest.c to return the nodename. 3. Do not quote spell checked stuff. This is not very intelligent but works better than before. 2. Fixes for relative path components. (paths that start with a .) 1. Fixed so that Rawmode() is not called when we are not editing (breaks running under emacs) V5.20.01, 11/15/90. 12. Recognize environment variables too. 11. Always start in Rawmode() 10. don't try to execute binary files using the bourne shell. 9. Vi change to end of line updates correctly now. 8. Prompt in continuation lines. 7. Prompt in if statements fix. 6. System V ^C works right now. 5. ^Z works correctly in bindings 4. Better error messages for variables. 3. dinit() now is more robust. 2. Added aux2.0 patches. 1. Fixed bug with arrow key bindings in ed.screen.c. All the keys were bound to up-history! V5.20.00, 11/10/90. 26. Fixed system V and POSIX time reporting. 25. Fixed ed.screen.c so that it does not use malloc(). 24. Fixed SIGWINCH on the iris 23. Fixed ed.screen.c, so that settc works correcly. It used to set the termcap with a string that was allocated from the stack! 22. Fixed listing of commands, where the last command was not checked. 21. Fixed which command. It did not work for \, if command was aliased. 20. Eliminated CSH4.3 define. You've had enough time to upgrade from 4.2 19. Fixed GotTermCaps to be called only once. 18. Added bindkey -r 17. Attributes were not getting reset correctly. 16. history -t does not print the time-stamp. 15. AddXkey, works now for single character xkeys. 14. filetype() knows better about symlinks. 13. ls-F works with filenames that have metachars 12. Completion/spelling works with quoted things. 11. Fixed refresh bug. Repeat by: On an intelligent terminal that has insert and delete chars (xterm) > orphan > vi orphan.c > ^P^P 10. Fixed so that if we don't have a tty on stdin editing is disabled. 9. Check for nested process forking, to avoid loops such as: > alias foo 'set bar=`foo`' > foo 8. Fix setting of AsciiOnly (Per) 7. tw.spell.c, defined F_OK for systems that don't have it. 6. ourwait.h had typo in ifdef. 5. BSD compilers need a cast to int for enums used in switches (Matthew Day) 4. Found the cause for the core-dump in long backquote expansions (Mark Davies) 3. Some externs in ed.h needed to be truly externs... reported by Mark Davies, bug appeared only on hp9000s800. 2. Changed $tcsh, and $version strings. 1. Added internal sprintf function, renamed putchar to CSHputchar, printf to CSHprintf, sprintf to CSHsprintf. V5.19.02, 10/23/90. 36. Added /etc/cshrc for the SGI irises. 35. Added expand-variables function. 34. Documented $time in tcsh.man 33. Shell variables are now 'recognized' when expanding/listing things. 32. ls-F does not eat the last slash on the / directory! 31. Strings bound to keys are printed inside double quotes. 30. History now remembers the exact line, not just an unparsed version of the tokens. 29. Renamed itoa to Itoa(), so things in libc that use itoa() don't break. 28. Ported to IBM aix/ps2. 27. Fixed eval so that it forks when the output is piped Repeat by: > who | grep $user > eval who | grep $user 26. Fixed so that 'nice ' does not nice or fork. Consider doing: nice cd /tmp && rm *.c (don't try it!!!) [what actually happens is that nice has to fork, then the child executes chdir, and the parent stays where it was] 25. Added Dan's patches (nls, builtin bindkey, fixes to the editor). 24. Added aix370, migrate, getspath, getspath, getxvers, setxvers 23. Added builtin echotc, removed sl and el. 22. Ported to 4.4 BSD. This involved changing the way lots of flags worked, cleanup of the SVID stuff, and addition of more compilation flags. Now POSIX can work whed BSD is defined... 21. Fixed so that el, sl work. Actually they are to be removed soon and be replaced with echotc. 20. Avoid the Quoted Space hack for alias when printing jobs! [aliases to the same name avoid further alias expansion, by inserting a quoted space in front of the command. In SHORT_STRINGS QUOTE is the 15th bit so print ignores it thinking it is the end of the string. So we just skip the Quoted Space....] Repeat by: > set notify > alias ls ls -F > (ls) & Prints Exit 0 ( Should print: Exit 0 ( ls -F ) 19. Fixed so that "", '', ``, all produce ': Command not found', and not the spurious messages. Repeat by: > set path = (/bin /usr/bin .... .) > "" /some/path/name/: Command not found 18. Enable the use of macros if they exist and NLS is defined. NLS code is not ready yet. 17. Fixed rmstar and continue_jobs code, so that they are not compile options, but shell variables. Now you need to 'set rmstar' to get enable rmstar. 16. Fixed SIGWINCH, SIGWINDOW, and setting of li, co, that broke suntools, and others. Repeat by: Start a shelltool of size other than 80x34 > telltc > stty -a do not report the same number of lines and columns. 15. More fixes to tw.parse.c, and tw.spell.c. 14. More fixes to sh.char.c. 13. Fixed coredump caused by ``. 12. Fixed spell-line code.. 11. Trapped SIGCHLD in sh.sem.c, while forking. Still there is a small race, but the probability of happening is smaller! 10. Fixed sh.char.c to be ISO compliant. 9. Added expand-glob 8. Fixed ourwait.h for little endians. 7. Fixed that foreach i (^D expands correctly. 6. Fixed so that listmax is ignored in ls-F. 5. Fixed spelling correction so that single letter words and words that contain globbing chars do not get spell checked. 4. Changed NeXT HOSTTYPE to next, since all HOSTTYPE's are lower case. 3. Fixed symmetry, and changed symmetry HOSTTYPE from sequent to symmetry. 2. Added boldfacing, underlining chars. 1. Added IRIX3.3.1 support. V5.19.01, 9/26/90. 7. Brought the README file up-to-date, and changed the bug report address to point to me. 6. sh.lex.c. In addla(), overflow computation was wrong. 5. Fixed SHELLPATH, to be set correctly when $SHELL is not set. 4. Fixed print statement in sh.proc.c that contained \215. 3. Fixed the Makefile and MAKEDIFFS so that a tahoe diff can be made. 2. RS6000: hacked around execv bug, and ed.init.c warning. 1. Editor should not be enabled when we don't have a tty. Index: head/contrib/tcsh/ed.chared.c =================================================================== --- head/contrib/tcsh/ed.chared.c (revision 315951) +++ head/contrib/tcsh/ed.chared.c (revision 315952) @@ -1,3898 +1,3898 @@ /* $Header: /p/tcsh/cvsroot/tcsh/ed.chared.c,v 3.103 2015/08/19 14:29:55 christos Exp $ */ /* * ed.chared.c: Character editing functions. */ /*- * Copyright (c) 1980, 1991 The 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. 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 REGENTS 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. */ /* Bjorn Knutsson @ Thu Jun 24 19:02:17 1999 e_dabbrev_expand() did not do proper completion if quoted spaces were present in the string being completed. Exemple: # echo hello\ world hello world # echo h # echo hello\ Correct behavior is: # echo h # echo hello\ world The same problem occured if spaces were present in a string withing quotation marks. Example: # echo "hello world" hello world # echo "h # echo "hello The former problem could be solved with minor modifications of c_preword() and c_endword(). The latter, however, required a significant rewrite of c_preword(), since quoted strings must be parsed from start to end to determine if a given character is inside or outside the quotation marks. Compare the following two strings: # echo \"" 'foo \' bar\" " 'foo \' bar\ # echo '\"" 'foo \' bar\" \"" foo ' bar" The only difference between the two echo lines is in the first character after the echo command. The result is either one or three arguments. */ #include "sh.h" RCSID("$tcsh: ed.chared.c,v 3.103 2015/08/19 14:29:55 christos Exp $") #include "ed.h" #include "tw.h" #include "ed.defns.h" /* #define SDEBUG */ #define TCSHOP_NOP 0x00 #define TCSHOP_DELETE 0x01 #define TCSHOP_INSERT 0x02 #define TCSHOP_CHANGE 0x04 #define CHAR_FWD 0 #define CHAR_BACK 1 /* * vi word treatment * from: Gert-Jan Vons */ #define C_CLASS_WHITE 1 #define C_CLASS_WORD 2 #define C_CLASS_OTHER 3 static Char *InsertPos = InputBuf; /* Where insertion starts */ static Char *ActionPos = 0; /* Where action begins */ static int ActionFlag = TCSHOP_NOP; /* What delayed action to take */ /* * Word search state */ static int searchdir = F_UP_SEARCH_HIST; /* Direction of last search */ static struct Strbuf patbuf; /* = Strbuf_INIT; Search target */ /* * Char search state */ static int srch_dir = CHAR_FWD; /* Direction of last search */ static Char srch_char = 0; /* Search target */ /* all routines that start with c_ are private to this set of routines */ static void c_alternativ_key_map (int); void c_insert (int); void c_delafter (int); void c_delbefore (int); static int c_to_class (Char); static Char *c_prev_word (Char *, Char *, int); static Char *c_next_word (Char *, Char *, int); static Char *c_number (Char *, int *, int); static Char *c_expand (Char *); static int c_excl (Char *); static int c_substitute (void); static void c_delfini (void); static int c_hmatch (Char *); static void c_hsetpat (void); #ifdef COMMENT static void c_get_word (Char **, Char **); #endif static Char *c_preword (Char *, Char *, int, Char *); static Char *c_nexword (Char *, Char *, int); static Char *c_endword (Char *, Char *, int, Char *); static Char *c_eword (Char *, Char *, int); static void c_push_kill (Char *, Char *); static void c_save_inputbuf (void); static CCRETVAL c_search_line (Char *, int); static CCRETVAL v_repeat_srch (int); static CCRETVAL e_inc_search (int); #ifdef notyet static CCRETVAL e_insert_str (Char *); #endif static CCRETVAL v_search (int); static CCRETVAL v_csearch_fwd (Char, int, int); static CCRETVAL v_action (int); static CCRETVAL v_csearch_back (Char, int, int); static void c_alternativ_key_map(int state) { switch (state) { case 0: CurrentKeyMap = CcKeyMap; break; case 1: CurrentKeyMap = CcAltMap; break; default: return; } AltKeyMap = (Char) state; } void c_insert(int num) { Char *cp; if (LastChar + num >= InputLim) return; /* can't go past end of buffer */ if (Cursor < LastChar) { /* if I must move chars */ for (cp = LastChar; cp >= Cursor; cp--) cp[num] = *cp; if (Mark && Mark > Cursor) Mark += num; } LastChar += num; } void c_delafter(int num) { Char *cp, *kp = NULL; if (num > LastChar - Cursor) num = (int) (LastChar - Cursor); /* bounds check */ if (num > 0) { /* if I can delete anything */ if (VImode) { kp = UndoBuf; /* Set Up for VI undo command */ UndoAction = TCSHOP_INSERT; UndoSize = num; UndoPtr = Cursor; for (cp = Cursor; cp <= LastChar; cp++) { *kp++ = *cp; /* Save deleted chars into undobuf */ *cp = cp[num]; } } else for (cp = Cursor; cp + num <= LastChar; cp++) *cp = cp[num]; LastChar -= num; /* Mark was within the range of the deleted word? */ if (Mark && Mark > Cursor && Mark <= Cursor+num) Mark = Cursor; /* Mark after the deleted word? */ else if (Mark && Mark > Cursor) Mark -= num; } #ifdef notdef else { /* * XXX: We don't want to do that. In emacs mode overwrite should be * sticky. I am not sure how that affects vi mode */ inputmode = MODE_INSERT; } #endif /* notdef */ } void c_delbefore(int num) /* delete before dot, with bounds checking */ { Char *cp, *kp = NULL; if (num > Cursor - InputBuf) num = (int) (Cursor - InputBuf); /* bounds check */ if (num > 0) { /* if I can delete anything */ if (VImode) { kp = UndoBuf; /* Set Up for VI undo command */ UndoAction = TCSHOP_INSERT; UndoSize = num; UndoPtr = Cursor - num; for (cp = Cursor - num; cp <= LastChar; cp++) { *kp++ = *cp; *cp = cp[num]; } } else for (cp = Cursor - num; cp + num <= LastChar; cp++) *cp = cp[num]; LastChar -= num; Cursor -= num; /* Mark was within the range of the deleted word? */ if (Mark && Mark > Cursor && Mark <= Cursor+num) Mark = Cursor; /* Mark after the deleted word? */ else if (Mark && Mark > Cursor) Mark -= num; } } static Char * c_preword(Char *p, Char *low, int n, Char *delim) { while (n--) { Char *prev = low; Char *new; while (prev < p) { /* Skip initial non-word chars */ if (!Strchr(delim, *prev) || *(prev-1) == (Char)'\\') break; prev++; } new = prev; while (new < p) { prev = new; new = c_endword(prev-1, p, 1, delim); /* Skip to next non-word char */ new++; /* Step away from end of word */ while (new <= p) { /* Skip trailing non-word chars */ if (!Strchr(delim, *new) || *(new-1) == (Char)'\\') break; new++; } } p = prev; /* Set to previous word start */ } if (p < low) p = low; return (p); } /* * c_to_class() returns the class of the given character. * * This is used to make the c_prev_word(), c_next_word() and c_eword() functions * work like vi's, which classify characters. A word is a sequence of * characters belonging to the same class, classes being defined as * follows: * * 1/ whitespace * 2/ alphanumeric chars, + underscore * 3/ others */ static int c_to_class(Char ch) { if (Isspace(ch)) return C_CLASS_WHITE; if (isword(ch)) return C_CLASS_WORD; return C_CLASS_OTHER; } static Char * c_prev_word(Char *p, Char *low, int n) { p--; if (!VImode) { while (n--) { while ((p >= low) && !isword(*p)) p--; while ((p >= low) && isword(*p)) p--; } /* cp now points to one character before the word */ p++; if (p < low) p = low; /* cp now points where we want it */ return(p); } while (n--) { int c_class; if (p < low) break; /* scan until beginning of current word (may be all whitespace!) */ c_class = c_to_class(*p); while ((p >= low) && c_class == c_to_class(*p)) p--; /* if this was a non_whitespace word, we're ready */ if (c_class != C_CLASS_WHITE) continue; /* otherwise, move back to beginning of the word just found */ c_class = c_to_class(*p); while ((p >= low) && c_class == c_to_class(*p)) p--; } p++; /* correct overshoot */ return (p); } static Char * c_next_word(Char *p, Char *high, int n) { if (!VImode) { while (n--) { while ((p < high) && !isword(*p)) p++; while ((p < high) && isword(*p)) p++; } if (p > high) p = high; /* p now points where we want it */ return(p); } while (n--) { int c_class; if (p >= high) break; /* scan until end of current word (may be all whitespace!) */ c_class = c_to_class(*p); while ((p < high) && c_class == c_to_class(*p)) p++; /* if this was all whitespace, we're ready */ if (c_class == C_CLASS_WHITE) continue; /* if we've found white-space at the end of the word, skip it */ while ((p < high) && c_to_class(*p) == C_CLASS_WHITE) p++; } p--; /* correct overshoot */ return (p); } static Char * c_nexword(Char *p, Char *high, int n) { while (n--) { while ((p < high) && !Isspace(*p)) p++; while ((p < high) && Isspace(*p)) p++; } if (p > high) p = high; /* p now points where we want it */ return(p); } /* * Expand-History (originally "Magic-Space") code added by * Ray Moody * this is a neat, but odd, addition. */ /* * c_number: Ignore character p points to, return number appearing after that. * A '$' by itself means a big number; "$-" is for negative; '^' means 1. * Return p pointing to last char used. */ /* * dval is the number to subtract from for things like $-3 */ static Char * c_number(Char *p, int *num, int dval) { int i; int sign = 1; if (*++p == '^') { *num = 1; return(p); } if (*p == '$') { if (*++p != '-') { *num = INT_MAX; /* Handle $ */ return(--p); } sign = -1; /* Handle $- */ ++p; } for (i = 0; *p >= '0' && *p <= '9'; i = 10 * i + *p++ - '0') continue; *num = (sign < 0 ? dval - i : i); return(--p); } /* * excl_expand: There is an excl to be expanded to p -- do the right thing * with it and return a version of p advanced over the expanded stuff. Also, * update tsh_cur and related things as appropriate... */ static Char * c_expand(Char *p) { Char *q; struct Hist *h = Histlist.Hnext; struct wordent *l; int i, from, to, dval; int all_dig; int been_once = 0; Char *op = p; Char *buf; size_t buf_len; Char *modbuf; buf = NULL; if (!h) goto excl_err; excl_sw: switch (*(q = p + 1)) { case '^': buf = expand_lex(&h->Hlex, 1, 1); break; case '$': if ((l = (h->Hlex).prev) != 0) buf = expand_lex(l->prev->prev, 0, 0); break; case '*': buf = expand_lex(&h->Hlex, 1, INT_MAX); break; default: if (been_once) { /* unknown argument */ /* assume it's a modifier, e.g. !foo:h, and get whole cmd */ buf = expand_lex(&h->Hlex, 0, INT_MAX); q -= 2; break; } been_once = 1; if (*q == ':') /* short form: !:arg */ --q; if (HIST != '\0' && *q != HIST) { /* * Search for a space, tab, or colon. See if we have a number (as * in !1234:xyz). Remember the number. */ for (i = 0, all_dig = 1; *q != ' ' && *q != '\t' && *q != ':' && q < Cursor; q++) { /* * PWP: !-4 is a valid history argument too, therefore the test * is if not a digit, or not a - as the first character. */ if ((*q < '0' || *q > '9') && (*q != '-' || q != p + 1)) all_dig = 0; else if (*q == '-') all_dig = 2;/* we are sneeky about this */ else i = 10 * i + *q - '0'; } --q; /* * If we have a number, search for event i. Otherwise, search for * a named event (as in !foo). (In this case, I is the length of * the named event). */ if (all_dig) { if (all_dig == 2) i = -i; /* make it negitive */ if (i < 0) /* if !-4 (for example) */ i = eventno + 1 + i; /* remember: i is < 0 */ for (; h; h = h->Hnext) { if (h->Hnum == i) break; } } else { for (i = (int) (q - p); h; h = h->Hnext) { if ((l = &h->Hlex) != 0) { if (!Strncmp(p + 1, l->next->word, (size_t) i)) break; } } } } if (!h) goto excl_err; if (q[1] == ':' || q[1] == '-' || q[1] == '*' || q[1] == '$' || q[1] == '^') { /* get some args */ p = q[1] == ':' ? ++q : q; /* * Go handle !foo:* */ if ((q[1] < '0' || q[1] > '9') && q[1] != '-' && q[1] != '$' && q[1] != '^') goto excl_sw; /* * Go handle !foo:$ */ if (q[1] == '$' && (q[2] != '-' || q[3] < '0' || q[3] > '9')) goto excl_sw; /* * Count up the number of words in this event. Store it in dval. * Dval will be fed to number. */ dval = 0; if ((l = h->Hlex.prev) != 0) { for (l = l->prev; l != h->Hlex.next; l = l->prev, dval++) continue; } if (!dval) goto excl_err; if (q[1] == '-') from = 0; else q = c_number(q, &from, dval); if (q[1] == '-') { ++q; if ((q[1] < '0' || q[1] > '9') && q[1] != '$') to = dval - 1; else q = c_number(q, &to, dval); } else if (q[1] == '*') { ++q; to = INT_MAX; } else { to = from; } if (from < 0 || to < from) goto excl_err; buf = expand_lex(&h->Hlex, from, to); } else /* get whole cmd */ buf = expand_lex(&h->Hlex, 0, INT_MAX); break; } if (buf == NULL) buf = SAVE(""); /* * Apply modifiers, if any. */ if (q[1] == ':') { modbuf = buf; while (q[1] == ':' && modbuf != NULL) { switch (q[2]) { case 'r': case 'e': case 'h': case 't': case 'q': case 'x': case 'u': case 'l': if ((modbuf = domod(buf, (int) q[2])) != NULL) { xfree(buf); buf = modbuf; } ++q; break; case 'a': case 'g': /* Not implemented; this needs to be done before expanding * lex. We don't have the words available to us anymore. */ ++q; break; case 'p': /* Ok */ ++q; break; case '\0': break; default: ++q; break; } if (q[1]) ++q; } } buf_len = Strlen(buf); /* * Now replace the text from op to q inclusive with the text from buf. */ q++; /* * Now replace text non-inclusively like a real CS major! */ if (LastChar + buf_len - (q - op) >= InputLim) goto excl_err; (void) memmove(op + buf_len, q, (LastChar - q) * sizeof(Char)); LastChar += buf_len - (q - op); Cursor += buf_len - (q - op); (void) memcpy(op, buf, buf_len * sizeof(Char)); *LastChar = '\0'; xfree(buf); return op + buf_len; excl_err: xfree(buf); SoundBeep(); return(op + 1); } /* * c_excl: An excl has been found at point p -- back up and find some white * space (or the beginning of the buffer) and properly expand all the excl's * from there up to the current cursor position. We also avoid (trying to) * expanding '>!' * Returns number of expansions attempted (doesn't matter whether they succeeded * or not). */ static int c_excl(Char *p) { int i; Char *q; int nr_exp; /* * if />[SPC TAB]*![SPC TAB]/, back up p to just after the >. otherwise, * back p up to just before the current word. */ if ((p[1] == ' ' || p[1] == '\t') && (p[-1] == ' ' || p[-1] == '\t' || p[-1] == '>')) { for (q = p - 1; q > InputBuf && (*q == ' ' || *q == '\t'); --q) continue; if (*q == '>') ++p; } else { while (*p != ' ' && *p != '\t' && p > InputBuf) --p; } /* * Forever: Look for history char. (Stop looking when we find the cursor.) * Count backslashes. If odd, skip history char. Expand if even number of * backslashes. */ nr_exp = 0; for (;;) { if (HIST != '\0') while (*p != HIST && p < Cursor) ++p; for (i = 1; (p - i) >= InputBuf && p[-i] == '\\'; i++) continue; if (i % 2 == 0) ++p; if (p >= Cursor) /* all done */ return nr_exp; if (i % 2 == 1) { p = c_expand(p); ++nr_exp; } } } static int c_substitute(void) { Char *p; int nr_exp; /* * Start p out one character before the cursor. Move it backwards looking * for white space, the beginning of the line, or a history character. */ for (p = Cursor - 1; p > InputBuf && *p != ' ' && *p != '\t' && *p && *p != HIST; --p) continue; /* * If we found a history character, go expand it. */ - if (HIST != '\0' && *p == HIST) + if (p >= InputBuf && HIST != '\0' && *p == HIST) nr_exp = c_excl(p); else nr_exp = 0; Refresh(); return nr_exp; } static void c_delfini(void) /* Finish up delete action */ { int Size; if (ActionFlag & TCSHOP_INSERT) c_alternativ_key_map(0); ActionFlag = TCSHOP_NOP; if (ActionPos == 0) return; UndoAction = TCSHOP_INSERT; if (Cursor > ActionPos) { Size = (int) (Cursor-ActionPos); c_delbefore(Size); RefCursor(); } else if (Cursor < ActionPos) { Size = (int)(ActionPos-Cursor); c_delafter(Size); } else { Size = 1; c_delafter(Size); } UndoPtr = Cursor; UndoSize = Size; } static Char * c_endword(Char *p, Char *high, int n, Char *delim) { Char inquote = 0; p++; while (n--) { while (p < high) { /* Skip non-word chars */ if (!Strchr(delim, *p) || *(p-1) == (Char)'\\') break; p++; } while (p < high) { /* Skip string */ if ((*p == (Char)'\'' || *p == (Char)'"')) { /* Quotation marks? */ if (inquote || *(p-1) != (Char)'\\') { /* Should it be honored? */ if (inquote == 0) inquote = *p; else if (inquote == *p) inquote = 0; } } /* Break if unquoted non-word char */ if (!inquote && Strchr(delim, *p) && *(p-1) != (Char)'\\') break; p++; } } p--; return(p); } static Char * c_eword(Char *p, Char *high, int n) { p++; while (n--) { int c_class; if (p >= high) break; /* scan until end of current word (may be all whitespace!) */ c_class = c_to_class(*p); while ((p < high) && c_class == c_to_class(*p)) p++; /* if this was a non_whitespace word, we're ready */ if (c_class != C_CLASS_WHITE) continue; /* otherwise, move to the end of the word just found */ c_class = c_to_class(*p); while ((p < high) && c_class == c_to_class(*p)) p++; } p--; return(p); } /* Set the max length of the kill ring */ void SetKillRing(int max) { CStr *new; int count, i, j; if (max < 1) max = 1; /* no ring, but always one buffer */ if (max == KillRingMax) return; new = xcalloc(max, sizeof(CStr)); if (KillRing != NULL) { if (KillRingLen != 0) { if (max >= KillRingLen) { count = KillRingLen; j = KillPos; } else { count = max; j = (KillPos - count + KillRingLen) % KillRingLen; } for (i = 0; i < KillRingLen; i++) { if (i < count) /* copy latest */ new[i] = KillRing[j]; else /* free the others */ xfree(KillRing[j].buf); j = (j + 1) % KillRingLen; } KillRingLen = count; KillPos = count % max; YankPos = count - 1; } xfree(KillRing); } KillRing = new; KillRingMax = max; } /* Push string from start upto (but not including) end onto kill ring */ static void c_push_kill(Char *start, Char *end) { CStr save, *pos; Char *dp, *cp, *kp; int len = end - start, i, j, k; /* Check for duplicates? */ if (KillRingLen > 0 && (dp = varval(STRkilldup)) != STRNULL) { YankPos = (KillPos - 1 + KillRingLen) % KillRingLen; if (eq(dp, STRerase)) { /* erase earlier one (actually move up) */ j = YankPos; for (i = 0; i < KillRingLen; i++) { if (Strncmp(KillRing[j].buf, start, (size_t) len) == 0 && KillRing[j].buf[len] == '\0') { save = KillRing[j]; for ( ; i > 0; i--) { k = j; j = (j + 1) % KillRingLen; KillRing[k] = KillRing[j]; } KillRing[j] = save; return; } j = (j - 1 + KillRingLen) % KillRingLen; } } else if (eq(dp, STRall)) { /* skip if any earlier */ for (i = 0; i < KillRingLen; i++) if (Strncmp(KillRing[i].buf, start, (size_t) len) == 0 && KillRing[i].buf[len] == '\0') return; } else if (eq(dp, STRprev)) { /* skip if immediately previous */ j = YankPos; if (Strncmp(KillRing[j].buf, start, (size_t) len) == 0 && KillRing[j].buf[len] == '\0') return; } } /* No duplicate, go ahead and push */ len++; /* need space for '\0' */ YankPos = KillPos; if (KillRingLen < KillRingMax) KillRingLen++; pos = &KillRing[KillPos]; KillPos = (KillPos + 1) % KillRingMax; if (pos->len < len) { pos->buf = xrealloc(pos->buf, len * sizeof(Char)); pos->len = len; } cp = start; kp = pos->buf; while (cp < end) *kp++ = *cp++; *kp = '\0'; } /* Save InputBuf etc in SavedBuf etc for restore after cmd exec */ static void c_save_inputbuf(void) { SavedBuf.len = 0; Strbuf_append(&SavedBuf, InputBuf); Strbuf_terminate(&SavedBuf); LastSaved = LastChar - InputBuf; CursSaved = Cursor - InputBuf; HistSaved = Hist_num; RestoreSaved = 1; } CCRETVAL GetHistLine(void) { struct Hist *hp; int h; if (Hist_num == 0) { /* if really the current line */ if (HistBuf.s != NULL) copyn(InputBuf, HistBuf.s, INBUFSIZE);/*FIXBUF*/ else *InputBuf = '\0'; LastChar = InputBuf + HistBuf.len; #ifdef KSHVI if (VImode) Cursor = InputBuf; else #endif /* KSHVI */ Cursor = LastChar; return(CC_REFRESH); } hp = Histlist.Hnext; if (hp == NULL) return(CC_ERROR); for (h = 1; h < Hist_num; h++) { if ((hp->Hnext) == NULL) { Hist_num = h; return(CC_ERROR); } hp = hp->Hnext; } if (HistLit && hp->histline) { copyn(InputBuf, hp->histline, INBUFSIZE);/*FIXBUF*/ CurrentHistLit = 1; } else { Char *p; p = sprlex(&hp->Hlex); copyn(InputBuf, p, sizeof(InputBuf) / sizeof(Char));/*FIXBUF*/ xfree(p); CurrentHistLit = 0; } LastChar = Strend(InputBuf); if (LastChar > InputBuf) { if (LastChar[-1] == '\n') LastChar--; #if 0 if (LastChar[-1] == ' ') LastChar--; #endif if (LastChar < InputBuf) LastChar = InputBuf; } #ifdef KSHVI if (VImode) Cursor = InputBuf; else #endif /* KSHVI */ Cursor = LastChar; return(CC_REFRESH); } static CCRETVAL c_search_line(Char *pattern, int dir) { Char *cp; size_t len; len = Strlen(pattern); if (dir == F_UP_SEARCH_HIST) { for (cp = Cursor; cp >= InputBuf; cp--) if (Strncmp(cp, pattern, len) == 0 || Gmatch(cp, pattern)) { Cursor = cp; return(CC_NORM); } return(CC_ERROR); } else { for (cp = Cursor; *cp != '\0' && cp < InputLim; cp++) if (Strncmp(cp, pattern, len) == 0 || Gmatch(cp, pattern)) { Cursor = cp; return(CC_NORM); } return(CC_ERROR); } } static CCRETVAL e_inc_search(int dir) { static const Char STRfwd[] = { 'f', 'w', 'd', '\0' }, STRbck[] = { 'b', 'c', 'k', '\0' }; static Char pchar = ':'; /* ':' = normal, '?' = failed */ static Char endcmd[2]; const Char *cp; Char ch, *oldCursor = Cursor, oldpchar = pchar; CCRETVAL ret = CC_NORM; int oldHist_num = Hist_num, oldpatlen = patbuf.len, newdir = dir, done, redo; if (LastChar + sizeof(STRfwd)/sizeof(Char) + 2 + patbuf.len >= InputLim) return(CC_ERROR); for (;;) { if (patbuf.len == 0) { /* first round */ pchar = ':'; Strbuf_append1(&patbuf, '*'); } done = redo = 0; *LastChar++ = '\n'; for (cp = newdir == F_UP_SEARCH_HIST ? STRbck : STRfwd; *cp; *LastChar++ = *cp++) continue; *LastChar++ = pchar; for (cp = &patbuf.s[1]; cp < &patbuf.s[patbuf.len]; *LastChar++ = *cp++) continue; *LastChar = '\0'; if (adrof(STRhighlight) && pchar == ':') { /* if the no-glob-search patch is applied, remove the - 1 below */ IncMatchLen = patbuf.len - 1; ClearLines(); ClearDisp(); } Refresh(); if (GetNextChar(&ch) != 1) return(e_send_eof(0)); switch (ch > NT_NUM_KEYS ? F_INSERT : CurrentKeyMap[(unsigned char) ch]) { case F_INSERT: case F_DIGIT: case F_MAGIC_SPACE: if (LastChar + 1 >= InputLim) /*FIXBUF*/ SoundBeep(); else { Strbuf_append1(&patbuf, ch); *LastChar++ = ch; *LastChar = '\0'; Refresh(); } break; case F_INC_FWD: newdir = F_DOWN_SEARCH_HIST; redo++; break; case F_INC_BACK: newdir = F_UP_SEARCH_HIST; redo++; break; case F_DELPREV: if (patbuf.len > 1) done++; else SoundBeep(); break; default: switch (ASC(ch)) { case 0007: /* ^G: Abort */ ret = CC_ERROR; done++; break; case 0027: /* ^W: Append word */ /* No can do if globbing characters in pattern */ for (cp = &patbuf.s[1]; ; cp++) if (cp >= &patbuf.s[patbuf.len]) { Cursor += patbuf.len - 1; cp = c_next_word(Cursor, LastChar, 1); while (Cursor < cp && *Cursor != '\n') { if (LastChar + 1 >= InputLim) {/*FIXBUF*/ SoundBeep(); break; } Strbuf_append1(&patbuf, *Cursor); *LastChar++ = *Cursor++; } Cursor = oldCursor; *LastChar = '\0'; Refresh(); break; } else if (isglob(*cp)) { SoundBeep(); break; } break; default: /* Terminate and execute cmd */ endcmd[0] = ch; PushMacro(endcmd); /*FALLTHROUGH*/ case 0033: /* ESC: Terminate */ ret = CC_REFRESH; done++; break; } break; } while (LastChar > InputBuf && *LastChar != '\n') *LastChar-- = '\0'; *LastChar = '\0'; if (!done) { /* Can't search if unmatched '[' */ for (cp = &patbuf.s[patbuf.len - 1], ch = ']'; cp > patbuf.s; cp--) if (*cp == '[' || *cp == ']') { ch = *cp; break; } if (patbuf.len > 1 && ch != '[') { if (redo && newdir == dir) { if (pchar == '?') { /* wrap around */ Hist_num = newdir == F_UP_SEARCH_HIST ? 0 : INT_MAX; if (GetHistLine() == CC_ERROR) /* Hist_num was fixed by first call */ (void) GetHistLine(); Cursor = newdir == F_UP_SEARCH_HIST ? LastChar : InputBuf; } else Cursor += newdir == F_UP_SEARCH_HIST ? -1 : 1; } Strbuf_append1(&patbuf, '*'); Strbuf_terminate(&patbuf); if (Cursor < InputBuf || Cursor > LastChar || (ret = c_search_line(&patbuf.s[1], newdir)) == CC_ERROR) { LastCmd = (KEYCMD) newdir; /* avoid c_hsetpat */ ret = newdir == F_UP_SEARCH_HIST ? e_up_search_hist(0) : e_down_search_hist(0); if (ret != CC_ERROR) { Cursor = newdir == F_UP_SEARCH_HIST ? LastChar : InputBuf; (void) c_search_line(&patbuf.s[1], newdir); } } patbuf.s[--patbuf.len] = '\0'; if (ret == CC_ERROR) { SoundBeep(); if (Hist_num != oldHist_num) { Hist_num = oldHist_num; if (GetHistLine() == CC_ERROR) return(CC_ERROR); } Cursor = oldCursor; pchar = '?'; } else { pchar = ':'; } } ret = e_inc_search(newdir); if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') { /* break abort of failed search at last non-failed */ ret = CC_NORM; } } if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) { /* restore on normal return or error exit */ pchar = oldpchar; patbuf.len = oldpatlen; if (Hist_num != oldHist_num) { Hist_num = oldHist_num; if (GetHistLine() == CC_ERROR) return(CC_ERROR); } Cursor = oldCursor; if (ret == CC_ERROR) Refresh(); } if (done || ret != CC_NORM) return(ret); } } static CCRETVAL v_search(int dir) { struct Strbuf tmpbuf = Strbuf_INIT; Char ch; Char *oldbuf; Char *oldlc, *oldc; cleanup_push(&tmpbuf, Strbuf_cleanup); oldbuf = Strsave(InputBuf); cleanup_push(oldbuf, xfree); oldlc = LastChar; oldc = Cursor; Strbuf_append1(&tmpbuf, '*'); InputBuf[0] = '\0'; LastChar = InputBuf; Cursor = InputBuf; searchdir = dir; c_insert(2); /* prompt + '\n' */ *Cursor++ = '\n'; *Cursor++ = dir == F_UP_SEARCH_HIST ? '?' : '/'; Refresh(); for (ch = 0;ch == 0;) { if (GetNextChar(&ch) != 1) { cleanup_until(&tmpbuf); return(e_send_eof(0)); } switch (ASC(ch)) { case 0010: /* Delete and backspace */ case 0177: if (tmpbuf.len > 1) { *Cursor-- = '\0'; LastChar = Cursor; tmpbuf.len--; } else { copyn(InputBuf, oldbuf, INBUFSIZE);/*FIXBUF*/ LastChar = oldlc; Cursor = oldc; cleanup_until(&tmpbuf); return(CC_REFRESH); } Refresh(); ch = 0; break; case 0033: /* ESC */ #ifdef IS_ASCII case '\r': /* Newline */ case '\n': #else case '\012': /* ASCII Line feed */ case '\015': /* ASCII (or EBCDIC) Return */ #endif break; default: Strbuf_append1(&tmpbuf, ch); *Cursor++ = ch; LastChar = Cursor; Refresh(); ch = 0; break; } } cleanup_until(oldbuf); if (tmpbuf.len == 1) { /* * Use the old pattern, but wild-card it. */ if (patbuf.len == 0) { InputBuf[0] = '\0'; LastChar = InputBuf; Cursor = InputBuf; Refresh(); cleanup_until(&tmpbuf); return(CC_ERROR); } if (patbuf.s[0] != '*') { oldbuf = Strsave(patbuf.s); patbuf.len = 0; Strbuf_append1(&patbuf, '*'); Strbuf_append(&patbuf, oldbuf); xfree(oldbuf); Strbuf_append1(&patbuf, '*'); Strbuf_terminate(&patbuf); } } else { Strbuf_append1(&tmpbuf, '*'); Strbuf_terminate(&tmpbuf); patbuf.len = 0; Strbuf_append(&patbuf, tmpbuf.s); Strbuf_terminate(&patbuf); } cleanup_until(&tmpbuf); LastCmd = (KEYCMD) dir; /* avoid c_hsetpat */ Cursor = LastChar = InputBuf; if ((dir == F_UP_SEARCH_HIST ? e_up_search_hist(0) : e_down_search_hist(0)) == CC_ERROR) { Refresh(); return(CC_ERROR); } else { if (ASC(ch) == 0033) { Refresh(); *LastChar++ = '\n'; *LastChar = '\0'; PastBottom(); return(CC_NEWLINE); } else return(CC_REFRESH); } } /* * semi-PUBLIC routines. Any routine that is of type CCRETVAL is an * entry point, called from the CcKeyMap indirected into the * CcFuncTbl array. */ /*ARGSUSED*/ CCRETVAL v_cmd_mode(Char c) { USE(c); InsertPos = 0; ActionFlag = TCSHOP_NOP; /* [Esc] cancels pending action */ ActionPos = 0; DoingArg = 0; if (UndoPtr > Cursor) UndoSize = (int)(UndoPtr - Cursor); else UndoSize = (int)(Cursor - UndoPtr); inputmode = MODE_INSERT; c_alternativ_key_map(1); #ifdef notdef /* * We don't want to move the cursor, because all the editing * commands don't include the character under the cursor. */ if (Cursor > InputBuf) Cursor--; #endif RefCursor(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_unassigned(Char c) { /* bound to keys that arn't really assigned */ USE(c); SoundBeep(); flush(); return(CC_NORM); } #ifdef notyet static CCRETVAL e_insert_str(Char *c) { int i, n; n = Strlen(c); if (LastChar + Argument * n >= InputLim) return(CC_ERROR); /* end of buffer space */ if (inputmode != MODE_INSERT) { c_delafter(Argument * Strlen(c)); } c_insert(Argument * n); while (Argument--) { for (i = 0; i < n; i++) *Cursor++ = c[i]; } Refresh(); return(CC_NORM); } #endif CCRETVAL e_insert(Char c) { #ifndef SHORT_STRINGS c &= ASCII; /* no meta chars ever */ #endif if (!c) return(CC_ERROR); /* no NULs in the input ever!! */ if (LastChar + Argument >= InputLim) return(CC_ERROR); /* end of buffer space */ if (Argument == 1) { /* How was this optimized ???? */ if (inputmode != MODE_INSERT) { UndoBuf[UndoSize++] = *Cursor; UndoBuf[UndoSize] = '\0'; c_delafter(1); /* Do NOT use the saving ONE */ } c_insert(1); *Cursor++ = (Char) c; DoingArg = 0; /* just in case */ RefPlusOne(1); /* fast refresh for one char. */ } else { if (inputmode != MODE_INSERT) { int i; for(i = 0; i < Argument; i++) UndoBuf[UndoSize++] = *(Cursor + i); UndoBuf[UndoSize] = '\0'; c_delafter(Argument); /* Do NOT use the saving ONE */ } c_insert(Argument); while (Argument--) *Cursor++ = (Char) c; Refresh(); } if (inputmode == MODE_REPLACE_1) (void) v_cmd_mode(0); return(CC_NORM); } int InsertStr(Char *s) /* insert ASCIZ s at cursor (for complete) */ { int len; if ((len = (int) Strlen(s)) <= 0) return -1; if (LastChar + len >= InputLim) return -1; /* end of buffer space */ c_insert(len); while (len--) *Cursor++ = *s++; return 0; } void DeleteBack(int n) /* delete the n characters before . */ { if (n <= 0) return; if (Cursor >= &InputBuf[n]) { c_delbefore(n); /* delete before dot */ } } CCRETVAL e_digit(Char c) /* gray magic here */ { if (!Isdigit(c)) return(CC_ERROR); /* no NULs in the input ever!! */ if (DoingArg) { /* if doing an arg, add this in... */ if (LastCmd == F_ARGFOUR) /* if last command was ^U */ Argument = c - '0'; else { if (Argument > 1000000) return CC_ERROR; Argument = (Argument * 10) + (c - '0'); } return(CC_ARGHACK); } else { if (LastChar + 1 >= InputLim) return CC_ERROR; /* end of buffer space */ if (inputmode != MODE_INSERT) { UndoBuf[UndoSize++] = *Cursor; UndoBuf[UndoSize] = '\0'; c_delafter(1); /* Do NOT use the saving ONE */ } c_insert(1); *Cursor++ = (Char) c; DoingArg = 0; /* just in case */ RefPlusOne(1); /* fast refresh for one char. */ } return(CC_NORM); } CCRETVAL e_argdigit(Char c) /* for ESC-n */ { #ifdef IS_ASCII c &= ASCII; #else c = CTL_ESC(ASC(c) & ASCII); /* stripping for EBCDIC done the ASCII way */ #endif if (!Isdigit(c)) return(CC_ERROR); /* no NULs in the input ever!! */ if (DoingArg) { /* if doing an arg, add this in... */ if (Argument > 1000000) return CC_ERROR; Argument = (Argument * 10) + (c - '0'); } else { /* else starting an argument */ Argument = c - '0'; DoingArg = 1; } return(CC_ARGHACK); } CCRETVAL v_zero(Char c) /* command mode 0 for vi */ { if (DoingArg) { /* if doing an arg, add this in... */ if (Argument > 1000000) return CC_ERROR; Argument = (Argument * 10) + (c - '0'); return(CC_ARGHACK); } else { /* else starting an argument */ Cursor = InputBuf; if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } RefCursor(); /* move the cursor */ return(CC_NORM); } } /*ARGSUSED*/ CCRETVAL e_newline(Char c) { /* always ignore argument */ USE(c); if (adrof(STRhighlight) && MarkIsSet) { MarkIsSet = 0; ClearLines(); ClearDisp(); Refresh(); } MarkIsSet = 0; /* PastBottom(); NOW done in ed.inputl.c */ *LastChar++ = '\n'; /* for the benefit of CSH */ *LastChar = '\0'; /* just in case */ if (VImode) InsertPos = InputBuf; /* Reset editing position */ return(CC_NEWLINE); } /*ARGSUSED*/ CCRETVAL e_newline_hold(Char c) { USE(c); c_save_inputbuf(); HistSaved = 0; *LastChar++ = '\n'; /* for the benefit of CSH */ *LastChar = '\0'; /* just in case */ return(CC_NEWLINE); } /*ARGSUSED*/ CCRETVAL e_newline_down_hist(Char c) { USE(c); if (Hist_num > 1) { HistSaved = Hist_num; } *LastChar++ = '\n'; /* for the benefit of CSH */ *LastChar = '\0'; /* just in case */ return(CC_NEWLINE); } /*ARGSUSED*/ CCRETVAL e_send_eof(Char c) { /* for when ^D is ONLY send-eof */ USE(c); PastBottom(); *LastChar = '\0'; /* just in case */ return(CC_EOF); } /*ARGSUSED*/ CCRETVAL e_complete(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_COMPLETE); } /*ARGSUSED*/ CCRETVAL e_complete_back(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_COMPLETE_BACK); } /*ARGSUSED*/ CCRETVAL e_complete_fwd(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_COMPLETE_FWD); } /*ARGSUSED*/ CCRETVAL e_complete_all(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_COMPLETE_ALL); } /*ARGSUSED*/ CCRETVAL v_cm_complete(Char c) { USE(c); if (Cursor < LastChar) Cursor++; *LastChar = '\0'; /* just in case */ return(CC_COMPLETE); } /*ARGSUSED*/ CCRETVAL e_toggle_hist(Char c) { struct Hist *hp; int h; USE(c); *LastChar = '\0'; /* just in case */ if (Hist_num <= 0) { return CC_ERROR; } hp = Histlist.Hnext; if (hp == NULL) { /* this is only if no history */ return(CC_ERROR); } for (h = 1; h < Hist_num; h++) hp = hp->Hnext; if (!CurrentHistLit) { if (hp->histline) { copyn(InputBuf, hp->histline, INBUFSIZE);/*FIXBUF*/ CurrentHistLit = 1; } else { return CC_ERROR; } } else { Char *p; p = sprlex(&hp->Hlex); copyn(InputBuf, p, sizeof(InputBuf) / sizeof(Char));/*FIXBUF*/ xfree(p); CurrentHistLit = 0; } LastChar = Strend(InputBuf); if (LastChar > InputBuf) { if (LastChar[-1] == '\n') LastChar--; if (LastChar[-1] == ' ') LastChar--; if (LastChar < InputBuf) LastChar = InputBuf; } #ifdef KSHVI if (VImode) Cursor = InputBuf; else #endif /* KSHVI */ Cursor = LastChar; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_up_hist(Char c) { Char beep = 0; USE(c); UndoAction = TCSHOP_NOP; *LastChar = '\0'; /* just in case */ if (Hist_num == 0) { /* save the current buffer away */ HistBuf.len = 0; Strbuf_append(&HistBuf, InputBuf); Strbuf_terminate(&HistBuf); } Hist_num += Argument; if (GetHistLine() == CC_ERROR) { beep = 1; (void) GetHistLine(); /* Hist_num was fixed by first call */ } Refresh(); if (beep) return(CC_ERROR); else return(CC_NORM); /* was CC_UP_HIST */ } /*ARGSUSED*/ CCRETVAL e_down_hist(Char c) { USE(c); UndoAction = TCSHOP_NOP; *LastChar = '\0'; /* just in case */ Hist_num -= Argument; if (Hist_num < 0) { Hist_num = 0; return(CC_ERROR); /* make it beep */ } return(GetHistLine()); } /* * c_hmatch() return True if the pattern matches the prefix */ static int c_hmatch(Char *str) { if (Strncmp(patbuf.s, str, patbuf.len) == 0) return 1; return Gmatch(str, patbuf.s); } /* * c_hsetpat(): Set the history seatch pattern */ static void c_hsetpat(void) { if (LastCmd != F_UP_SEARCH_HIST && LastCmd != F_DOWN_SEARCH_HIST) { patbuf.len = 0; Strbuf_appendn(&patbuf, InputBuf, Cursor - InputBuf); Strbuf_terminate(&patbuf); } #ifdef SDEBUG xprintf("\nHist_num = %d\n", Hist_num); xprintf("patlen = %d\n", (int)patbuf.len); xprintf("patbuf = \"%S\"\n", patbuf.s); xprintf("Cursor %d LastChar %d\n", Cursor - InputBuf, LastChar - InputBuf); #endif } /*ARGSUSED*/ CCRETVAL e_up_search_hist(Char c) { struct Hist *hp; int h; int found = 0; USE(c); ActionFlag = TCSHOP_NOP; UndoAction = TCSHOP_NOP; *LastChar = '\0'; /* just in case */ if (Hist_num < 0) { #ifdef DEBUG_EDIT xprintf("%s: e_up_search_hist(): Hist_num < 0; resetting.\n", progname); #endif Hist_num = 0; return(CC_ERROR); } if (Hist_num == 0) { HistBuf.len = 0; Strbuf_append(&HistBuf, InputBuf); Strbuf_terminate(&HistBuf); } hp = Histlist.Hnext; if (hp == NULL) return(CC_ERROR); c_hsetpat(); /* Set search pattern !! */ for (h = 1; h <= Hist_num; h++) hp = hp->Hnext; while (hp != NULL) { Char *hl; int matched; if (hp->histline == NULL) hp->histline = sprlex(&hp->Hlex); if (HistLit) hl = hp->histline; else { hl = sprlex(&hp->Hlex); cleanup_push(hl, xfree); } #ifdef SDEBUG xprintf("Comparing with \"%S\"\n", hl); #endif matched = (Strncmp(hl, InputBuf, (size_t) (LastChar - InputBuf)) || hl[LastChar-InputBuf]) && c_hmatch(hl); if (!HistLit) cleanup_until(hl); if (matched) { found++; break; } h++; hp = hp->Hnext; } if (!found) { #ifdef SDEBUG xprintf("not found\n"); #endif return(CC_ERROR); } Hist_num = h; return(GetHistLine()); } /*ARGSUSED*/ CCRETVAL e_down_search_hist(Char c) { struct Hist *hp; int h; int found = 0; USE(c); ActionFlag = TCSHOP_NOP; UndoAction = TCSHOP_NOP; *LastChar = '\0'; /* just in case */ if (Hist_num == 0) return(CC_ERROR); hp = Histlist.Hnext; if (hp == 0) return(CC_ERROR); c_hsetpat(); /* Set search pattern !! */ for (h = 1; h < Hist_num && hp; h++) { Char *hl; if (hp->histline == NULL) hp->histline = sprlex(&hp->Hlex); if (HistLit) hl = hp->histline; else { hl = sprlex(&hp->Hlex); cleanup_push(hl, xfree); } #ifdef SDEBUG xprintf("Comparing with \"%S\"\n", hl); #endif if ((Strncmp(hl, InputBuf, (size_t) (LastChar - InputBuf)) || hl[LastChar-InputBuf]) && c_hmatch(hl)) found = h; if (!HistLit) cleanup_until(hl); hp = hp->Hnext; } if (!found) { /* is it the current history number? */ if (!c_hmatch(HistBuf.s)) { #ifdef SDEBUG xprintf("not found\n"); #endif return(CC_ERROR); } } Hist_num = found; return(GetHistLine()); } /*ARGSUSED*/ CCRETVAL e_helpme(Char c) { USE(c); PastBottom(); *LastChar = '\0'; /* just in case */ return(CC_HELPME); } /*ARGSUSED*/ CCRETVAL e_correct(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_CORRECT); } /*ARGSUSED*/ CCRETVAL e_correctl(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_CORRECT_L); } /*ARGSUSED*/ CCRETVAL e_run_fg_editor(Char c) { struct process *pp; USE(c); if ((pp = find_stop_ed()) != NULL) { /* save our editor state so we can restore it */ c_save_inputbuf(); Hist_num = 0; /* for the history commands */ /* put the tty in a sane mode */ PastBottom(); (void) Cookedmode(); /* make sure the tty is set up correctly */ /* do it! */ fg_proc_entry(pp); (void) Rawmode(); /* go on */ Refresh(); RestoreSaved = 0; HistSaved = 0; } return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_list_choices(Char c) { USE(c); PastBottom(); *LastChar = '\0'; /* just in case */ return(CC_LIST_CHOICES); } /*ARGSUSED*/ CCRETVAL e_list_all(Char c) { USE(c); PastBottom(); *LastChar = '\0'; /* just in case */ return(CC_LIST_ALL); } /*ARGSUSED*/ CCRETVAL e_list_glob(Char c) { USE(c); PastBottom(); *LastChar = '\0'; /* just in case */ return(CC_LIST_GLOB); } /*ARGSUSED*/ CCRETVAL e_expand_glob(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_EXPAND_GLOB); } /*ARGSUSED*/ CCRETVAL e_normalize_path(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_NORMALIZE_PATH); } /*ARGSUSED*/ CCRETVAL e_normalize_command(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_NORMALIZE_COMMAND); } /*ARGSUSED*/ CCRETVAL e_expand_vars(Char c) { USE(c); *LastChar = '\0'; /* just in case */ return(CC_EXPAND_VARS); } /*ARGSUSED*/ CCRETVAL e_which(Char c) { /* do a fast command line which(1) */ USE(c); c_save_inputbuf(); Hist_num = 0; /* for the history commands */ PastBottom(); *LastChar = '\0'; /* just in case */ return(CC_WHICH); } /*ARGSUSED*/ CCRETVAL e_last_item(Char c) { /* insert the last element of the prev. cmd */ struct Hist *hp; struct wordent *wp, *firstp; int i; Char *expanded; USE(c); if (Argument <= 0) return(CC_ERROR); hp = Histlist.Hnext; if (hp == NULL) { /* this is only if no history */ return(CC_ERROR); } wp = (hp->Hlex).prev; if (wp->prev == (struct wordent *) NULL) return(CC_ERROR); /* an empty history entry */ firstp = (hp->Hlex).next; /* back up arg words in lex */ for (i = 0; i < Argument && wp != firstp; i++) { wp = wp->prev; } expanded = expand_lex(wp->prev, 0, i - 1); if (InsertStr(expanded)) { xfree(expanded); return(CC_ERROR); } xfree(expanded); return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_dabbrev_expand(Char c) { /* expand to preceding word matching prefix */ Char *cp, *ncp, *bp; struct Hist *hp; int arg = 0, i; size_t len = 0; int found = 0; Char *hbuf; static int oldevent, hist, word; static Char *start, *oldcursor; USE(c); if (Argument <= 0) return(CC_ERROR); cp = c_preword(Cursor, InputBuf, 1, STRshwordsep); if (cp == Cursor || Isspace(*cp)) return(CC_ERROR); hbuf = NULL; hp = Histlist.Hnext; bp = InputBuf; if (Argument == 1 && eventno == oldevent && cp == start && Cursor == oldcursor && patbuf.len > 0 && Strncmp(patbuf.s, cp, patbuf.len) == 0){ /* continue previous search - go to last match (hist/word) */ if (hist != 0) { /* need to move up history */ for (i = 1; i < hist && hp != NULL; i++) hp = hp->Hnext; if (hp == NULL) /* "can't happen" */ goto err_hbuf; hbuf = expand_lex(&hp->Hlex, 0, INT_MAX); cp = Strend(hbuf); bp = hbuf; hp = hp->Hnext; } cp = c_preword(cp, bp, word, STRshwordsep); } else { /* starting new search */ oldevent = eventno; start = cp; patbuf.len = 0; Strbuf_appendn(&patbuf, cp, Cursor - cp); hist = 0; word = 0; } while (!found) { ncp = c_preword(cp, bp, 1, STRshwordsep); if (ncp == cp || Isspace(*ncp)) { /* beginning of line */ hist++; word = 0; if (hp == NULL) goto err_hbuf; hbuf = expand_lex(&hp->Hlex, 0, INT_MAX); cp = Strend(hbuf); bp = hbuf; hp = hp->Hnext; continue; } else { word++; len = c_endword(ncp-1, cp, 1, STRshwordsep) - ncp + 1; cp = ncp; } if (len > patbuf.len && Strncmp(cp, patbuf.s, patbuf.len) == 0) { /* We don't fully check distinct matches as Gnuemacs does: */ if (Argument > 1) { /* just count matches */ if (++arg >= Argument) found++; } else { /* match if distinct from previous */ if (len != (size_t)(Cursor - start) || Strncmp(cp, start, len) != 0) found++; } } } if (LastChar + len - (Cursor - start) >= InputLim) goto err_hbuf; /* no room */ DeleteBack(Cursor - start); c_insert(len); while (len--) *Cursor++ = *cp++; oldcursor = Cursor; xfree(hbuf); return(CC_REFRESH); err_hbuf: xfree(hbuf); return CC_ERROR; } /*ARGSUSED*/ CCRETVAL e_yank_kill(Char c) { /* almost like GnuEmacs */ int len; Char *kp, *cp; USE(c); if (KillRingLen == 0) /* nothing killed */ return(CC_ERROR); len = Strlen(KillRing[YankPos].buf); if (LastChar + len >= InputLim) return(CC_ERROR); /* end of buffer space */ /* else */ cp = Cursor; /* for speed */ c_insert(len); /* open the space, */ for (kp = KillRing[YankPos].buf; *kp; kp++) /* copy the chars */ *cp++ = *kp; if (Argument == 1) { /* if no arg */ Mark = Cursor; /* mark at beginning, cursor at end */ Cursor = cp; } else { Mark = cp; /* else cursor at beginning, mark at end */ } if (adrof(STRhighlight) && MarkIsSet) { ClearLines(); ClearDisp(); } MarkIsSet = 0; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_yank_pop(Char c) { /* almost like GnuEmacs */ int m_bef_c, del_len, ins_len; Char *kp, *cp; USE(c); #if 0 /* XXX This "should" be here, but doesn't work, since LastCmd gets set on CC_ERROR and CC_ARGHACK, which it shouldn't(?). (But what about F_ARGFOUR?) I.e. if you hit M-y twice the second one will "succeed" even if the first one wasn't preceded by a yank, and giving an argument is impossible. Now we "succeed" regardless of previous command, which is wrong too of course. */ if (LastCmd != F_YANK_KILL && LastCmd != F_YANK_POP) return(CC_ERROR); #endif if (KillRingLen == 0) /* nothing killed */ return(CC_ERROR); YankPos -= Argument; while (YankPos < 0) YankPos += KillRingLen; YankPos %= KillRingLen; if (Cursor > Mark) { del_len = Cursor - Mark; m_bef_c = 1; } else { del_len = Mark - Cursor; m_bef_c = 0; } ins_len = Strlen(KillRing[YankPos].buf); if (LastChar + ins_len - del_len >= InputLim) return(CC_ERROR); /* end of buffer space */ if (m_bef_c) { c_delbefore(del_len); } else { c_delafter(del_len); } cp = Cursor; /* for speed */ c_insert(ins_len); /* open the space, */ for (kp = KillRing[YankPos].buf; *kp; kp++) /* copy the chars */ *cp++ = *kp; if (m_bef_c) { Mark = Cursor; /* mark at beginning, cursor at end */ Cursor = cp; } else { Mark = cp; /* else cursor at beginning, mark at end */ } if (adrof(STRhighlight) && MarkIsSet) { ClearLines(); ClearDisp(); } MarkIsSet = 0; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL v_delprev(Char c) /* Backspace key in insert mode */ { int rc; USE(c); rc = CC_ERROR; if (InsertPos != 0) { if (Argument <= Cursor - InsertPos) { c_delbefore(Argument); /* delete before */ rc = CC_REFRESH; } } return(rc); } /* v_delprev */ /*ARGSUSED*/ CCRETVAL e_delprev(Char c) { USE(c); if (Cursor > InputBuf) { c_delbefore(Argument); /* delete before dot */ return(CC_REFRESH); } else { return(CC_ERROR); } } /*ARGSUSED*/ CCRETVAL e_delwordprev(Char c) { Char *cp; USE(c); if (Cursor == InputBuf) return(CC_ERROR); /* else */ cp = c_prev_word(Cursor, InputBuf, Argument); c_push_kill(cp, Cursor); /* save the text */ c_delbefore((int)(Cursor - cp)); /* delete before dot */ return(CC_REFRESH); } /* DCS , 9 Oct 93 * * Changed the names of some of the ^D family of editor functions to * correspond to what they actually do and created new e_delnext_list * for completeness. * * Old names: New names: * * delete-char delete-char-or-eof * F_DELNEXT F_DELNEXT_EOF * e_delnext e_delnext_eof * edelnxt edelnxteof * delete-char-or-eof delete-char * F_DELNEXT_EOF F_DELNEXT * e_delnext_eof e_delnext * edelnxteof edelnxt * delete-char-or-list delete-char-or-list-or-eof * F_LIST_DELNEXT F_DELNEXT_LIST_EOF * e_list_delnext e_delnext_list_eof * edellsteof * (no old equivalent) delete-char-or-list * F_DELNEXT_LIST * e_delnext_list * e_delnxtlst */ /* added by mtk@ari.ncl.omron.co.jp (920818) */ /* rename e_delnext() -> e_delnext_eof() */ /*ARGSUSED*/ CCRETVAL e_delnext(Char c) { USE(c); if (Cursor == LastChar) {/* if I'm at the end */ if (!VImode) { return(CC_ERROR); } else { if (Cursor != InputBuf) Cursor--; else return(CC_ERROR); } } c_delafter(Argument); /* delete after dot */ if (Cursor > LastChar) Cursor = LastChar; /* bounds check */ return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_delnext_eof(Char c) { USE(c); if (Cursor == LastChar) {/* if I'm at the end */ if (!VImode) { if (Cursor == InputBuf) { /* if I'm also at the beginning */ so_write(STReof, 4);/* then do a EOF */ flush(); return(CC_EOF); } else return(CC_ERROR); } else { if (Cursor != InputBuf) Cursor--; else return(CC_ERROR); } } c_delafter(Argument); /* delete after dot */ if (Cursor > LastChar) Cursor = LastChar; /* bounds check */ return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_delnext_list(Char c) { USE(c); if (Cursor == LastChar) { /* if I'm at the end */ PastBottom(); *LastChar = '\0'; /* just in case */ return(CC_LIST_CHOICES); } else { c_delafter(Argument); /* delete after dot */ if (Cursor > LastChar) Cursor = LastChar; /* bounds check */ return(CC_REFRESH); } } /*ARGSUSED*/ CCRETVAL e_delnext_list_eof(Char c) { USE(c); if (Cursor == LastChar) { /* if I'm at the end */ if (Cursor == InputBuf) { /* if I'm also at the beginning */ so_write(STReof, 4);/* then do a EOF */ flush(); return(CC_EOF); } else { PastBottom(); *LastChar = '\0'; /* just in case */ return(CC_LIST_CHOICES); } } else { c_delafter(Argument); /* delete after dot */ if (Cursor > LastChar) Cursor = LastChar; /* bounds check */ return(CC_REFRESH); } } /*ARGSUSED*/ CCRETVAL e_list_eof(Char c) { CCRETVAL rv; USE(c); if (Cursor == LastChar && Cursor == InputBuf) { so_write(STReof, 4); /* then do a EOF */ flush(); rv = CC_EOF; } else { PastBottom(); *LastChar = '\0'; /* just in case */ rv = CC_LIST_CHOICES; } return rv; } /*ARGSUSED*/ CCRETVAL e_delwordnext(Char c) { Char *cp; USE(c); if (Cursor == LastChar) return(CC_ERROR); /* else */ cp = c_next_word(Cursor, LastChar, Argument); c_push_kill(Cursor, cp); /* save the text */ c_delafter((int)(cp - Cursor)); /* delete after dot */ if (Cursor > LastChar) Cursor = LastChar; /* bounds check */ return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_toend(Char c) { USE(c); Cursor = LastChar; if (VImode) if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } RefCursor(); /* move the cursor */ return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_tobeg(Char c) { USE(c); Cursor = InputBuf; if (VImode) { while (Isspace(*Cursor)) /* We want FIRST non space character */ Cursor++; if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } } RefCursor(); /* move the cursor */ return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_killend(Char c) { USE(c); c_push_kill(Cursor, LastChar); /* copy it */ LastChar = Cursor; /* zap! -- delete to end */ if (Mark > Cursor) Mark = Cursor; MarkIsSet = 0; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_killbeg(Char c) { USE(c); c_push_kill(InputBuf, Cursor); /* copy it */ c_delbefore((int)(Cursor - InputBuf)); if (Mark && Mark > Cursor) Mark -= Cursor-InputBuf; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_killall(Char c) { USE(c); c_push_kill(InputBuf, LastChar); /* copy it */ Cursor = Mark = LastChar = InputBuf; /* zap! -- delete all of it */ MarkIsSet = 0; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_killregion(Char c) { USE(c); if (!Mark) return(CC_ERROR); if (Mark > Cursor) { c_push_kill(Cursor, Mark); /* copy it */ c_delafter((int)(Mark - Cursor)); /* delete it - UNUSED BY VI mode */ Mark = Cursor; } else { /* mark is before cursor */ c_push_kill(Mark, Cursor); /* copy it */ c_delbefore((int)(Cursor - Mark)); } if (adrof(STRhighlight) && MarkIsSet) { ClearLines(); ClearDisp(); } MarkIsSet = 0; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_copyregion(Char c) { USE(c); if (!Mark) return(CC_ERROR); if (Mark > Cursor) { c_push_kill(Cursor, Mark); /* copy it */ } else { /* mark is before cursor */ c_push_kill(Mark, Cursor); /* copy it */ } return(CC_NORM); /* don't even need to Refresh() */ } /*ARGSUSED*/ CCRETVAL e_charswitch(Char cc) { Char c; USE(cc); /* do nothing if we are at beginning of line or have only one char */ if (Cursor == &InputBuf[0] || LastChar == &InputBuf[1]) { return(CC_ERROR); } if (Cursor < LastChar) { Cursor++; } c = Cursor[-2]; Cursor[-2] = Cursor[-1]; Cursor[-1] = c; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_gcharswitch(Char cc) { /* gosmacs style ^T */ Char c; USE(cc); if (Cursor > &InputBuf[1]) {/* must have at least two chars entered */ c = Cursor[-2]; Cursor[-2] = Cursor[-1]; Cursor[-1] = c; return(CC_REFRESH); } else { return(CC_ERROR); } } /*ARGSUSED*/ CCRETVAL e_charback(Char c) { USE(c); if (Cursor > InputBuf) { if (Argument > Cursor - InputBuf) Cursor = InputBuf; else Cursor -= Argument; if (VImode) if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } else { return(CC_ERROR); } } /*ARGSUSED*/ CCRETVAL v_wordback(Char c) { USE(c); if (Cursor == InputBuf) return(CC_ERROR); /* else */ Cursor = c_preword(Cursor, InputBuf, Argument, STRshwspace); /* bounds check */ if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_wordback(Char c) { USE(c); if (Cursor == InputBuf) return(CC_ERROR); /* else */ Cursor = c_prev_word(Cursor, InputBuf, Argument); /* bounds check */ if (VImode) if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_charfwd(Char c) { USE(c); if (Cursor < LastChar) { Cursor += Argument; if (Cursor > LastChar) Cursor = LastChar; if (VImode) if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } else { return(CC_ERROR); } } /*ARGSUSED*/ CCRETVAL e_wordfwd(Char c) { USE(c); if (Cursor == LastChar) return(CC_ERROR); /* else */ Cursor = c_next_word(Cursor, LastChar, Argument); if (VImode) if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_wordfwd(Char c) { USE(c); if (Cursor == LastChar) return(CC_ERROR); /* else */ Cursor = c_nexword(Cursor, LastChar, Argument); if (VImode) if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_wordbegnext(Char c) { USE(c); if (Cursor == LastChar) return(CC_ERROR); /* else */ Cursor = c_next_word(Cursor, LastChar, Argument); if (Cursor < LastChar) Cursor++; if (VImode) if (ActionFlag & TCSHOP_DELETE) { c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } /*ARGSUSED*/ static CCRETVAL v_repeat_srch(int c) { CCRETVAL rv = CC_ERROR; #ifdef SDEBUG xprintf("dir %d patlen %d patbuf %S\n", c, (int)patbuf.len, patbuf.s); #endif LastCmd = (KEYCMD) c; /* Hack to stop c_hsetpat */ LastChar = InputBuf; switch (c) { case F_DOWN_SEARCH_HIST: rv = e_down_search_hist(0); break; case F_UP_SEARCH_HIST: rv = e_up_search_hist(0); break; default: break; } return rv; } static CCRETVAL v_csearch_back(Char ch, int count, int tflag) { Char *cp; cp = Cursor; while (count--) { if (*cp == ch) cp--; while (cp > InputBuf && *cp != ch) cp--; } if (cp < InputBuf || (cp == InputBuf && *cp != ch)) return(CC_ERROR); if (*cp == ch && tflag) cp++; Cursor = cp; if (ActionFlag & TCSHOP_DELETE) { Cursor++; c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } static CCRETVAL v_csearch_fwd(Char ch, int count, int tflag) { Char *cp; cp = Cursor; while (count--) { if(*cp == ch) cp++; while (cp < LastChar && *cp != ch) cp++; } if (cp >= LastChar) return(CC_ERROR); if (*cp == ch && tflag) cp--; Cursor = cp; if (ActionFlag & TCSHOP_DELETE) { Cursor++; c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } /*ARGSUSED*/ static CCRETVAL v_action(int c) { Char *cp, *kp; if (ActionFlag == TCSHOP_DELETE) { ActionFlag = TCSHOP_NOP; ActionPos = 0; UndoSize = 0; kp = UndoBuf; for (cp = InputBuf; cp < LastChar; cp++) { *kp++ = *cp; UndoSize++; } UndoAction = TCSHOP_INSERT; UndoPtr = InputBuf; LastChar = InputBuf; Cursor = InputBuf; if (c & TCSHOP_INSERT) c_alternativ_key_map(0); return(CC_REFRESH); } #ifdef notdef else if (ActionFlag == TCSHOP_NOP) { #endif ActionPos = Cursor; ActionFlag = c; return(CC_ARGHACK); /* Do NOT clear out argument */ #ifdef notdef } else { ActionFlag = 0; ActionPos = 0; return(CC_ERROR); } #endif } #ifdef COMMENT /* by: Brian Allison */ static void c_get_word(Char **begin, Char **end) { Char *cp; cp = &Cursor[0]; while (Argument--) { while ((cp <= LastChar) && (isword(*cp))) cp++; *end = --cp; while ((cp >= InputBuf) && (isword(*cp))) cp--; *begin = ++cp; } } #endif /* COMMENT */ /*ARGSUSED*/ CCRETVAL e_uppercase(Char c) { Char *cp, *end; USE(c); end = c_next_word(Cursor, LastChar, Argument); for (cp = Cursor; cp < end; cp++) /* PWP: was cp=begin */ if (Islower(*cp)) *cp = Toupper(*cp); Cursor = end; if (Cursor > LastChar) Cursor = LastChar; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_capitalcase(Char c) { Char *cp, *end; USE(c); end = c_next_word(Cursor, LastChar, Argument); cp = Cursor; for (; cp < end; cp++) { if (Isalpha(*cp)) { if (Islower(*cp)) *cp = Toupper(*cp); cp++; break; } } for (; cp < end; cp++) if (Isupper(*cp)) *cp = Tolower(*cp); Cursor = end; if (Cursor > LastChar) Cursor = LastChar; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_lowercase(Char c) { Char *cp, *end; USE(c); end = c_next_word(Cursor, LastChar, Argument); for (cp = Cursor; cp < end; cp++) if (Isupper(*cp)) *cp = Tolower(*cp); Cursor = end; if (Cursor > LastChar) Cursor = LastChar; return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_set_mark(Char c) { USE(c); if (adrof(STRhighlight) && MarkIsSet && Mark != Cursor) { ClearLines(); ClearDisp(); Refresh(); } Mark = Cursor; MarkIsSet = 1; return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_exchange_mark(Char c) { Char *cp; USE(c); cp = Cursor; Cursor = Mark; Mark = cp; RefCursor(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_argfour(Char c) { /* multiply current argument by 4 */ USE(c); if (Argument > 1000000) return CC_ERROR; DoingArg = 1; Argument *= 4; return(CC_ARGHACK); } static void quote_mode_cleanup(void *unused) { USE(unused); QuoteModeOff(); } /*ARGSUSED*/ CCRETVAL e_quote(Char c) { Char ch; int num; USE(c); QuoteModeOn(); cleanup_push(&c, quote_mode_cleanup); /* Using &c just as a mark */ num = GetNextChar(&ch); cleanup_until(&c); if (num == 1) return e_insert(ch); else return e_send_eof(0); } /*ARGSUSED*/ CCRETVAL e_metanext(Char c) { USE(c); MetaNext = 1; return(CC_ARGHACK); /* preserve argument */ } #ifdef notdef /*ARGSUSED*/ CCRETVAL e_extendnext(Char c) { CurrentKeyMap = CcAltMap; return(CC_ARGHACK); /* preserve argument */ } #endif /*ARGSUSED*/ CCRETVAL v_insbeg(Char c) { /* move to beginning of line and start vi * insert mode */ USE(c); Cursor = InputBuf; InsertPos = Cursor; UndoPtr = Cursor; UndoAction = TCSHOP_DELETE; RefCursor(); /* move the cursor */ c_alternativ_key_map(0); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_replone(Char c) { /* vi mode overwrite one character */ USE(c); c_alternativ_key_map(0); inputmode = MODE_REPLACE_1; UndoAction = TCSHOP_CHANGE; /* Set Up for VI undo command */ UndoPtr = Cursor; UndoSize = 0; return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_replmode(Char c) { /* vi mode start overwriting */ USE(c); c_alternativ_key_map(0); inputmode = MODE_REPLACE; UndoAction = TCSHOP_CHANGE; /* Set Up for VI undo command */ UndoPtr = Cursor; UndoSize = 0; return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_substchar(Char c) { /* vi mode substitute for one char */ USE(c); c_delafter(Argument); c_alternativ_key_map(0); return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL v_substline(Char c) { /* vi mode replace whole line */ USE(c); (void) e_killall(0); c_alternativ_key_map(0); return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL v_chgtoend(Char c) { /* vi mode change to end of line */ USE(c); (void) e_killend(0); c_alternativ_key_map(0); return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL v_insert(Char c) { /* vi mode start inserting */ USE(c); c_alternativ_key_map(0); InsertPos = Cursor; UndoPtr = Cursor; UndoAction = TCSHOP_DELETE; return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_add(Char c) { /* vi mode start adding */ USE(c); c_alternativ_key_map(0); if (Cursor < LastChar) { Cursor++; if (Cursor > LastChar) Cursor = LastChar; RefCursor(); } InsertPos = Cursor; UndoPtr = Cursor; UndoAction = TCSHOP_DELETE; return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_addend(Char c) { /* vi mode to add at end of line */ USE(c); c_alternativ_key_map(0); Cursor = LastChar; InsertPos = LastChar; /* Mark where insertion begins */ UndoPtr = LastChar; UndoAction = TCSHOP_DELETE; RefCursor(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_change_case(Char cc) { Char c; USE(cc); if (Cursor < LastChar) { #ifndef WINNT_NATIVE c = *Cursor; #else c = CHAR & *Cursor; #endif /* WINNT_NATIVE */ if (Isupper(c)) *Cursor++ = Tolower(c); else if (Islower(c)) *Cursor++ = Toupper(c); else Cursor++; RefPlusOne(1); /* fast refresh for one char */ return(CC_NORM); } return(CC_ERROR); } /*ARGSUSED*/ CCRETVAL e_expand(Char c) { Char *p; USE(c); for (p = InputBuf; Isspace(*p); p++) continue; if (p == LastChar) return(CC_ERROR); justpr++; Expand++; return(e_newline(0)); } /*ARGSUSED*/ CCRETVAL e_startover(Char c) { /* erase all of current line, start again */ USE(c); ResetInLine(0); /* reset the input pointers */ return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_redisp(Char c) { USE(c); ClearLines(); ClearDisp(); return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_cleardisp(Char c) { USE(c); ClearScreen(); /* clear the whole real screen */ ClearDisp(); /* reset everything */ return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_tty_int(Char c) { USE(c); #if defined(_MINIX) || defined(WINNT_NATIVE) /* SAK PATCH: erase all of current line, start again */ ResetInLine(0); /* reset the input pointers */ xputchar('\n'); ClearDisp(); return (CC_REFRESH); #else /* !_MINIX && !WINNT_NATIVE */ /* do no editing */ return (CC_NORM); #endif /* _MINIX || WINNT_NATIVE */ } /* * From: ghazi@cesl.rutgers.edu (Kaveh R. Ghazi) * Function to send a character back to the input stream in cooked * mode. Only works if we have TIOCSTI */ /*ARGSUSED*/ CCRETVAL e_stuff_char(Char c) { #ifdef TIOCSTI int was_raw = Tty_raw_mode; char buf[MB_LEN_MAX]; size_t i, len; if (was_raw) (void) Cookedmode(); (void) xwrite(SHIN, "\n", 1); len = one_wctomb(buf, c); for (i = 0; i < len; i++) (void) ioctl(SHIN, TIOCSTI, (ioctl_t) &buf[i]); if (was_raw) (void) Rawmode(); return(e_redisp(c)); #else /* !TIOCSTI */ return(CC_ERROR); #endif /* !TIOCSTI */ } /*ARGSUSED*/ CCRETVAL e_insovr(Char c) { USE(c); inputmode = (inputmode == MODE_INSERT ? MODE_REPLACE : MODE_INSERT); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_tty_dsusp(Char c) { USE(c); /* do no editing */ return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_tty_flusho(Char c) { USE(c); /* do no editing */ return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_tty_quit(Char c) { USE(c); /* do no editing */ return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_tty_tsusp(Char c) { USE(c); /* do no editing */ return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_tty_stopo(Char c) { USE(c); /* do no editing */ return(CC_NORM); } /* returns the number of (attempted) expansions */ int ExpandHistory(void) { *LastChar = '\0'; /* just in case */ return c_substitute(); } /*ARGSUSED*/ CCRETVAL e_expand_history(Char c) { USE(c); (void)ExpandHistory(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_magic_space(Char c) { USE(c); *LastChar = '\0'; /* just in case */ (void)c_substitute(); return(e_insert(' ')); } /*ARGSUSED*/ CCRETVAL e_inc_fwd(Char c) { CCRETVAL ret; USE(c); patbuf.len = 0; MarkIsSet = 0; ret = e_inc_search(F_DOWN_SEARCH_HIST); if (adrof(STRhighlight) && IncMatchLen) { IncMatchLen = 0; ClearLines(); ClearDisp(); Refresh(); } IncMatchLen = 0; return ret; } /*ARGSUSED*/ CCRETVAL e_inc_back(Char c) { CCRETVAL ret; USE(c); patbuf.len = 0; MarkIsSet = 0; ret = e_inc_search(F_UP_SEARCH_HIST); if (adrof(STRhighlight) && IncMatchLen) { IncMatchLen = 0; ClearLines(); ClearDisp(); Refresh(); } IncMatchLen = 0; return ret; } /*ARGSUSED*/ CCRETVAL e_copyprev(Char c) { Char *cp, *oldc, *dp; USE(c); if (Cursor == InputBuf) return(CC_ERROR); /* else */ oldc = Cursor; /* does a bounds check */ cp = c_prev_word(Cursor, InputBuf, Argument); c_insert((int)(oldc - cp)); for (dp = oldc; cp < oldc && dp < LastChar; cp++) *dp++ = *cp; Cursor = dp; /* put cursor at end */ return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL e_tty_starto(Char c) { USE(c); /* do no editing */ return(CC_NORM); } /*ARGSUSED*/ CCRETVAL e_load_average(Char c) { USE(c); PastBottom(); #ifdef TIOCSTAT /* * Here we pass &c to the ioctl because some os's (NetBSD) expect it * there even if they don't use it. (lukem@netbsd.org) */ if (ioctl(SHIN, TIOCSTAT, (ioctl_t) &c) < 0) #endif xprintf("%s", CGETS(5, 1, "Load average unavailable\n")); return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL v_chgmeta(Char c) { USE(c); /* * Delete with insert == change: first we delete and then we leave in * insert mode. */ return(v_action(TCSHOP_DELETE|TCSHOP_INSERT)); } /*ARGSUSED*/ CCRETVAL v_delmeta(Char c) { USE(c); return(v_action(TCSHOP_DELETE)); } /*ARGSUSED*/ CCRETVAL v_endword(Char c) { USE(c); if (Cursor == LastChar) return(CC_ERROR); /* else */ Cursor = c_endword(Cursor, LastChar, Argument, STRshwspace); if (ActionFlag & TCSHOP_DELETE) { Cursor++; c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_eword(Char c) { USE(c); if (Cursor == LastChar) return(CC_ERROR); /* else */ Cursor = c_eword(Cursor, LastChar, Argument); if (ActionFlag & TCSHOP_DELETE) { Cursor++; c_delfini(); return(CC_REFRESH); } RefCursor(); return(CC_NORM); } /*ARGSUSED*/ CCRETVAL v_char_fwd(Char c) { Char ch; USE(c); if (GetNextChar(&ch) != 1) return e_send_eof(0); srch_dir = CHAR_FWD; srch_char = ch; return v_csearch_fwd(ch, Argument, 0); } /*ARGSUSED*/ CCRETVAL v_char_back(Char c) { Char ch; USE(c); if (GetNextChar(&ch) != 1) return e_send_eof(0); srch_dir = CHAR_BACK; srch_char = ch; return v_csearch_back(ch, Argument, 0); } /*ARGSUSED*/ CCRETVAL v_charto_fwd(Char c) { Char ch; USE(c); if (GetNextChar(&ch) != 1) return e_send_eof(0); return v_csearch_fwd(ch, Argument, 1); } /*ARGSUSED*/ CCRETVAL v_charto_back(Char c) { Char ch; USE(c); if (GetNextChar(&ch) != 1) return e_send_eof(0); return v_csearch_back(ch, Argument, 1); } /*ARGSUSED*/ CCRETVAL v_rchar_fwd(Char c) { USE(c); if (srch_char == 0) return CC_ERROR; return srch_dir == CHAR_FWD ? v_csearch_fwd(srch_char, Argument, 0) : v_csearch_back(srch_char, Argument, 0); } /*ARGSUSED*/ CCRETVAL v_rchar_back(Char c) { USE(c); if (srch_char == 0) return CC_ERROR; return srch_dir == CHAR_BACK ? v_csearch_fwd(srch_char, Argument, 0) : v_csearch_back(srch_char, Argument, 0); } /*ARGSUSED*/ CCRETVAL v_undo(Char c) { int loop; Char *kp, *cp; Char temp; int size; USE(c); switch (UndoAction) { case TCSHOP_DELETE|TCSHOP_INSERT: case TCSHOP_DELETE: if (UndoSize == 0) return(CC_NORM); cp = UndoPtr; kp = UndoBuf; for (loop=0; loop < UndoSize; loop++) /* copy the chars */ *kp++ = *cp++; /* into UndoBuf */ for (cp = UndoPtr; cp <= LastChar; cp++) *cp = cp[UndoSize]; LastChar -= UndoSize; Cursor = UndoPtr; UndoAction = TCSHOP_INSERT; break; case TCSHOP_INSERT: if (UndoSize == 0) return(CC_NORM); cp = UndoPtr; Cursor = UndoPtr; kp = UndoBuf; c_insert(UndoSize); /* open the space, */ for (loop = 0; loop < UndoSize; loop++) /* copy the chars */ *cp++ = *kp++; UndoAction = TCSHOP_DELETE; break; case TCSHOP_CHANGE: if (UndoSize == 0) return(CC_NORM); cp = UndoPtr; Cursor = UndoPtr; kp = UndoBuf; size = (int)(Cursor-LastChar); /* NOT NSL independant */ if (size < UndoSize) size = UndoSize; for(loop = 0; loop < size; loop++) { temp = *kp; *kp++ = *cp; *cp++ = temp; } break; default: return(CC_ERROR); } return(CC_REFRESH); } /*ARGSUSED*/ CCRETVAL v_ush_meta(Char c) { USE(c); return v_search(F_UP_SEARCH_HIST); } /*ARGSUSED*/ CCRETVAL v_dsh_meta(Char c) { USE(c); return v_search(F_DOWN_SEARCH_HIST); } /*ARGSUSED*/ CCRETVAL v_rsrch_fwd(Char c) { USE(c); if (patbuf.len == 0) return(CC_ERROR); return(v_repeat_srch(searchdir)); } /*ARGSUSED*/ CCRETVAL v_rsrch_back(Char c) { USE(c); if (patbuf.len == 0) return(CC_ERROR); return(v_repeat_srch(searchdir == F_UP_SEARCH_HIST ? F_DOWN_SEARCH_HIST : F_UP_SEARCH_HIST)); } #ifndef WINNT_NATIVE /* Since ed.defns.h is generated from ed.defns.c, these empty functions will keep the F_NUM_FNS consistent */ CCRETVAL e_copy_to_clipboard(Char c) { USE(c); return CC_ERROR; } CCRETVAL e_paste_from_clipboard(Char c) { USE(c); return (CC_ERROR); } CCRETVAL e_dosify_next(Char c) { USE(c); return (CC_ERROR); } CCRETVAL e_dosify_prev(Char c) { USE(c); return (CC_ERROR); } CCRETVAL e_page_up(Char c) { USE(c); return (CC_ERROR); } CCRETVAL e_page_down(Char c) { USE(c); return (CC_ERROR); } #endif /* !WINNT_NATIVE */ #ifdef notdef void MoveCursor(int n) /* move cursor + right - left char */ { Cursor = Cursor + n; if (Cursor < InputBuf) Cursor = InputBuf; if (Cursor > LastChar) Cursor = LastChar; return; } Char * GetCursor(void) { return(Cursor); } int PutCursor(Char *p) { if (p < InputBuf || p > LastChar) return 1; /* Error */ Cursor = p; return 0; } #endif Index: head/contrib/tcsh/sh.c =================================================================== --- head/contrib/tcsh/sh.c (revision 315951) +++ head/contrib/tcsh/sh.c (revision 315952) @@ -1,2540 +1,2544 @@ /* $Header: /p/tcsh/cvsroot/tcsh/sh.c,v 3.189 2016/09/12 16:33:54 christos Exp $ */ /* * sh.c: Main shell routines */ /*- * Copyright (c) 1980, 1991 The 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. 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 REGENTS 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. */ #define EXTERN /* Intern */ #include "sh.h" #ifndef lint char copyright[] = "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ RCSID("$tcsh: sh.c,v 3.189 2016/09/12 16:33:54 christos Exp $") #include "tc.h" #include "ed.h" #include "tw.h" extern int MapsAreInited; extern int NLSMapsAreInited; /* * C Shell * * Bill Joy, UC Berkeley, California, USA * October 1978, May 1980 * * Jim Kulp, IIASA, Laxenburg, Austria * April 1980 * * Filename recognition added: * Ken Greer, Ind. Consultant, Palo Alto CA * October 1983. * * Karl Kleinpaste, Computer Consoles, Inc. * Added precmd, periodic/tperiod, prompt changes, * directory stack hack, and login watch. * Sometime March 1983 - Feb 1984. * * Added scheduled commands, including the "sched" command, * plus the call to sched_run near the precmd et al * routines. * Upgraded scheduled events for running events while * sitting idle at command input. * * Paul Placeway, Ohio State * added stuff for running with twenex/inputl 9 Oct 1984. * * ported to Apple Unix (TM) (OREO) 26 -- 29 Jun 1987 */ jmp_buf_t reslab IZERO_STRUCT; struct wordent paraml IZERO_STRUCT; static const char tcshstr[] = "tcsh"; struct sigaction parintr; /* Parents interrupt catch */ struct sigaction parterm; /* Parents terminate catch */ #ifdef TESLA int do_logout = 0; #endif /* TESLA */ int use_fork = 0; /* use fork() instead of vfork()? */ /* * Magic pointer values. Used to specify other invalid conditions aside * from null. */ static Char INVCHAR; Char *INVPTR = &INVCHAR; Char **INVPPTR = &INVPTR; static int fast = 0; static int mflag = 0; static int prompt = 1; int enterhist = 0; int tellwhat = 0; time_t t_period; Char *ffile = NULL; int dolzero = 0; int insource = 0; int exitset = 0; static time_t chktim; /* Time mail last checked */ char *progname; int tcsh; /* * This preserves the input state of the shell. It is used by * st_save and st_restore to manupulate shell state. */ struct saved_state { int insource; int OLDSTD; int SHIN; int SHOUT; int SHDIAG; int intty; struct whyle *whyles; Char *gointr; Char *arginp; Char *evalp; Char **evalvec; Char *alvecp; Char **alvec; int onelflg; int enterhist; Char **argv; Char **av; Char HIST; int cantell; struct Bin B; int justpr; }; static int srccat (Char *, Char *); #ifndef WINNT_NATIVE static int srcfile (const char *, int, int, Char **); #else int srcfile (const char *, int, int, Char **); #endif /*WINNT_NATIVE*/ static void srcunit (int, int, int, Char **); static void mailchk (void); #ifndef _PATH_DEFPATH static Char **defaultpath (void); #endif static void record (void); static void st_save (struct saved_state *, int, int, Char **, Char **); static void st_restore (void *); int main (int, char **); #ifndef LOCALEDIR #define LOCALEDIR "/usr/share/locale" #endif #ifdef NLS_CATALOGS static void add_localedir_to_nlspath(const char *path) { static const char msgs_LOC[] = "/%L/LC_MESSAGES/%N.cat"; static const char msgs_lang[] = "/%l/LC_MESSAGES/%N.cat"; char *old; char *new, *new_p; size_t len; int add_LOC = 1; int add_lang = 1; char trypath[MAXPATHLEN]; struct stat st; if (path == NULL) return; (void) xsnprintf(trypath, sizeof(trypath), "%s/en/LC_MESSAGES/tcsh.cat", path); if (stat(trypath, &st) == -1) return; if ((old = getenv("NLSPATH")) != NULL) len = strlen(old) + 1; /* don't forget the colon. */ else len = 0; len += 2 * strlen(path) + sizeof(msgs_LOC) + sizeof(msgs_lang); /* includes the extra colon */ new = new_p = xcalloc(len, 1); if (old != NULL) { size_t pathlen = strlen(path); char *old_p; (void) xsnprintf(new_p, len, "%s", old); new_p += strlen(new_p); len -= new_p - new; /* Check if the paths we try to add are already present in NLSPATH. If so, note it by setting the appropriate flag to 0. */ for (old_p = old; old_p; old_p = strchr(old_p, ':'), old_p = old_p ? old_p + 1 : NULL) { if (strncmp(old_p, path, pathlen) != 0) continue; if (strncmp(old_p + pathlen, msgs_LOC, sizeof(msgs_LOC) - 1) == 0) add_LOC = 0; else if (strncmp(old_p + pathlen, msgs_lang, sizeof(msgs_lang) - 1) == 0) add_lang = 0; } } /* Add the message catalog paths not already present to NLSPATH. */ if (add_LOC || add_lang) (void) xsnprintf(new_p, len, "%s%s%s%s%s%s", old ? ":" : "", add_LOC ? path : "", add_LOC ? msgs_LOC : "", add_LOC && add_lang ? ":" : "", add_lang ? path : "", add_lang ? msgs_lang : ""); tsetenv(STRNLSPATH, str2short(new)); free(new); } #endif int main(int argc, char **argv) { int batch = 0; volatile int nexececho = 0; int nofile = 0; volatile int nverbose = 0; volatile int rdirs = 0; int quitit = 0; Char *cp; #ifdef AUTOLOGOUT Char *cp2; #endif char *tcp, *ttyn; int f, reenter; char **tempv; + const char *targinp = NULL; int osetintr; struct sigaction oparintr; #ifdef WINNT_NATIVE nt_init(); #endif /* WINNT_NATIVE */ (void)memset(&reslab, 0, sizeof(reslab)); #if defined(NLS_CATALOGS) && defined(LC_MESSAGES) (void) setlocale(LC_MESSAGES, ""); #endif /* NLS_CATALOGS && LC_MESSAGES */ #ifdef NLS # ifdef LC_CTYPE (void) setlocale(LC_CTYPE, ""); /* for iscntrl */ # endif /* LC_CTYPE */ #endif /* NLS */ STR_environ = blk2short(environ); environ = short2blk(STR_environ); /* So that we can free it */ #ifdef NLS_CATALOGS add_localedir_to_nlspath(LOCALEDIR); #endif nlsinit(); initlex(¶ml); #ifdef MALLOC_TRACE mal_setstatsfile(fdopen(dmove(xopen("/tmp/tcsh.trace", O_WRONLY|O_CREAT|O_LARGEFILE, 0666), 25), "w")); mal_trace(1); #endif /* MALLOC_TRACE */ #if !(defined(BSDTIMES) || defined(_SEQUENT_)) && defined(POSIX) # ifdef _SC_CLK_TCK clk_tck = (clock_t) sysconf(_SC_CLK_TCK); # else /* ! _SC_CLK_TCK */ # ifdef CLK_TCK clk_tck = CLK_TCK; # else /* !CLK_TCK */ clk_tck = HZ; # endif /* CLK_TCK */ # endif /* _SC_CLK_TCK */ #endif /* !BSDTIMES && POSIX */ settimes(); /* Immed. estab. timing base */ #ifdef TESLA do_logout = 0; #endif /* TESLA */ /* * Make sure we have 0, 1, 2 open * Otherwise `` jobs will not work... (From knaff@poly.polytechnique.fr) */ { do if ((f = xopen(_PATH_DEVNULL, O_RDONLY|O_LARGEFILE)) == -1 && (f = xopen("/", O_RDONLY|O_LARGEFILE)) == -1) exit(1); while (f < 3); xclose(f); } osinit(); /* Os dependent initialization */ { char *t; t = strrchr(argv[0], '/'); #ifdef WINNT_NATIVE { char *s = strrchr(argv[0], '\\'); if (s) t = s; } #endif /* WINNT_NATIVE */ t = t ? t + 1 : argv[0]; if (*t == '-') t++; progname = strsave((t && *t) ? t : tcshstr); /* never want a null */ tcsh = strncmp(progname, tcshstr, sizeof(tcshstr) - 1) == 0; } /* * Initialize non constant strings */ #ifdef _PATH_BSHELL STR_BSHELL = SAVE(_PATH_BSHELL); #endif #ifdef _PATH_TCSHELL STR_SHELLPATH = SAVE(_PATH_TCSHELL); #else # ifdef _PATH_CSHELL STR_SHELLPATH = SAVE(_PATH_CSHELL); # endif #endif STR_WORD_CHARS = SAVE(WORD_CHARS); STR_WORD_CHARS_VI = SAVE(WORD_CHARS_VI); HIST = '!'; HISTSUB = '^'; PRCH = tcsh ? '>' : '%'; /* to replace %# in $prompt for normal users */ PRCHROOT = '#'; /* likewise for root */ word_chars = STR_WORD_CHARS; bslash_quote = 0; /* PWP: do tcsh-style backslash quoting? */ anyerror = 1; /* for compatibility */ setcopy(STRanyerror, STRNULL, VAR_READWRITE); /* Default history size to 100 */ setcopy(STRhistory, str2short("100"), VAR_READWRITE); sethistory(100); tempv = argv; ffile = SAVE(tempv[0]); dolzero = 0; if (eq(ffile, STRaout)) /* A.out's are quittable */ quitit = 1; uid = getuid(); gid = getgid(); euid = geteuid(); egid = getegid(); /* * We are a login shell if: 1. we were invoked as - with * optional arguments 2. or we were invoked only with the -l flag */ loginsh = (**tempv == '-') || (argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' && tempv[1][2] == '\0'); #ifdef _VMS_POSIX /* No better way to find if we are a login shell */ if (!loginsh) { loginsh = (argc == 1 && getppid() == 1); **tempv = '-'; /* Avoid giving VMS an acidic stomach */ } #endif /* _VMS_POSIX */ if (loginsh && **tempv != '-') { char *argv0; /* * Mangle the argv space */ tempv[1][0] = '\0'; tempv[1][1] = '\0'; tempv[1] = NULL; argv0 = strspl("-", *tempv); *tempv = argv0; argc--; } if (loginsh) { (void) time(&chktim); setNS(STRloginsh); } NoNLSRebind = getenv("NOREBIND") != NULL; #ifdef NLS # ifdef SETLOCALEBUG dont_free = 1; # endif /* SETLOCALEBUG */ (void) setlocale(LC_ALL, ""); # ifdef LC_COLLATE (void) setlocale(LC_COLLATE, ""); # endif # ifdef SETLOCALEBUG dont_free = 0; # endif /* SETLOCALEBUG */ # ifdef STRCOLLBUG fix_strcoll_bug(); # endif /* STRCOLLBUG */ /* * On solaris ISO8859-1 contains no printable characters in the upper half * so we need to test only for MB_CUR_MAX == 1, otherwise for multi-byte * locales we are always AsciiOnly == 0. */ if (MB_CUR_MAX == 1) { int k; for (k = 0200; k <= 0377 && !isprint(CTL_ESC(k)); k++) continue; AsciiOnly = k > 0377; } else AsciiOnly = 0; #else AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; #endif /* NLS */ if (MapsAreInited && !NLSMapsAreInited) ed_InitNLSMaps(); ResetArrowKeys(); /* * Initialize for periodic command intervals. Also, initialize the dummy * tty list for login-watch. */ (void) time(&t_period); #ifndef HAVENOUTMP initwatch(); #endif /* !HAVENOUTMP */ #if defined(alliant) /* * From: Jim Pace * tcsh does not work properly on the alliants through an rlogin session. * The shell generally hangs. Also, reference to the controlling terminal * does not work ( ie: echo foo > /dev/tty ). * * A security feature was added to rlogind affecting FX/80's Concentrix * from revision 5.5.xx upwards (through 5.7 where this fix was implemented) * This security change also affects the FX/2800 series. * The security change to rlogind requires the process group of an rlogin * session become disassociated with the tty in rlogind. * * The changes needed are: * 1. set the process group * 2. reenable the control terminal */ if (loginsh && isatty(SHIN)) { ttyn = ttyname(SHIN); xclose(SHIN); SHIN = xopen(ttyn, O_RDWR|O_LARGEFILE); shpgrp = getpid(); (void) ioctl (SHIN, TIOCSPGRP, (ioctl_t) &shpgrp); (void) setpgid(0, shpgrp); } #endif /* alliant */ /* * 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. */ initdesc(); cdtohome = 1; setv(STRcdtohome, SAVE(""), VAR_READWRITE); /* * Get and set the tty now */ if ((ttyn = ttyname(SHIN)) != NULL) { /* * Could use rindex to get rid of other possible path components, but * hpux preserves the subdirectory /pty/ when storing the tty name in * utmp, so we keep it too. */ if (strncmp(ttyn, "/dev/", 5) == 0) setv(STRtty, cp = SAVE(ttyn + 5), VAR_READWRITE); else setv(STRtty, cp = SAVE(ttyn), VAR_READWRITE); } else setv(STRtty, cp = SAVE(""), VAR_READWRITE); /* * Initialize the shell variables. ARGV and PROMPT are initialized later. * STATUS is also munged in several places. CHILD is munged when * forking/waiting */ /* * 7-10-87 Paul Placeway autologout should be set ONLY on login shells and * on shells running as root. Out of these, autologout should NOT be set * for any psudo-terminals (this catches most window systems) and not for * any terminal running X windows. * * At Ohio State, we have had problems with a user having his X session * drop out from under him (on a Sun) because the shell in his master * xterm timed out and exited. * * Really, this should be done with a program external to the shell, that * watches for no activity (and NO running programs, such as dump) on a * terminal for a long peroid of time, and then SIGHUPS the shell on that * terminal. * * bugfix by Rich Salz : For root rsh things * allways first check to see if loginsh or really root, then do things * with ttyname() * * Also by Jean-Francois Lamy : check the * value of cp before using it! ("root can rsh too") * * PWP: keep the nested ifs; the order of the tests matters and a good * (smart) C compiler might re-arange things wrong. */ #ifdef AUTOLOGOUT # ifdef convex if (uid == 0) /* root always has a 15 minute autologout */ setcopy(STRautologout, STRrootdefautologout, VAR_READWRITE); else if (loginsh) /* users get autologout set to 0 */ setcopy(STRautologout, STR0, VAR_READWRITE); # else /* convex */ if (loginsh || (uid == 0)) { if (*cp) { /* only for login shells or root and we must have a tty */ if (((cp2 = Strrchr(cp, (Char) '/')) != NULL) && (Strncmp(cp, STRptssl, 3) != 0)) { cp2 = cp2 + 1; } else cp2 = cp; if (!(((Strncmp(cp2, STRtty, 3) == 0) && Isalpha(cp2[3])) || Strstr(cp, STRptssl) != NULL)) { if (getenv("DISPLAY") == NULL) { /* NOT on X window shells */ setcopy(STRautologout, STRdefautologout, VAR_READWRITE); } } } } # endif /* convex */ #endif /* AUTOLOGOUT */ sigset_interrupting(SIGALRM, queue_alrmcatch); setcopy(STRstatus, STR0, VAR_READWRITE); /* * get and set machine specific environment variables */ getmachine(); /* * Publish the selected echo style */ #if ECHO_STYLE != BSD_ECHO if (tcsh) { # if ECHO_STYLE == NONE_ECHO setcopy(STRecho_style, STRnone, VAR_READWRITE); # endif /* ECHO_STYLE == NONE_ECHO */ # if ECHO_STYLE == SYSV_ECHO setcopy(STRecho_style, STRsysv, VAR_READWRITE); # endif /* ECHO_STYLE == SYSV_ECHO */ # if ECHO_STYLE == BOTH_ECHO setcopy(STRecho_style, STRboth, VAR_READWRITE); # endif /* ECHO_STYLE == BOTH_ECHO */ } else #endif /* ECHO_STYLE != BSD_ECHO */ setcopy(STRecho_style, STRbsd, VAR_READWRITE); /* * increment the shell level. */ shlvl(1); #ifdef __ANDROID__ /* On Android, $HOME either isn't set or set to /data, a R/O location. Check for the environment variable EXTERNAL_STORAGE, which contains the mount point of the external storage (SD card, mostly). If EXTERNAL_STORAGE isn't set fall back to "/sdcard". Eventually override $HOME so the environment is on the same page. */ if (((tcp = getenv("HOME")) != NULL && strcmp (tcp, "/data") != 0) || (tcp = getenv("EXTERNAL_STORAGE")) != NULL) { cp = quote(SAVE(tcp)); } else cp = quote(SAVE("/sdcard")); tsetenv(STRKHOME, cp); #else if ((tcp = getenv("HOME")) != NULL) cp = quote(SAVE(tcp)); else cp = NULL; #endif if (cp == NULL) fast = 1; /* No home -> can't read scripts */ else setv(STRhome, cp, VAR_READWRITE); dinit(cp); /* dinit thinks that HOME == cwd in a login * shell */ /* * Grab other useful things from the environment. Should we grab * everything?? */ { char *cln, *cus, *cgr; struct passwd *pw; struct group *gr; #ifdef apollo int oid = getoid(); setv(STRoid, Itoa(oid, 0, 0), VAR_READWRITE); #endif /* apollo */ setv(STReuid, Itoa(euid, 0, 0), VAR_READWRITE); if ((pw = xgetpwuid(euid)) == NULL) setcopy(STReuser, STRunknown, VAR_READWRITE); else setcopy(STReuser, str2short(pw->pw_name), VAR_READWRITE); setv(STRuid, Itoa(uid, 0, 0), VAR_READWRITE); setv(STRgid, Itoa(gid, 0, 0), VAR_READWRITE); cln = getenv("LOGNAME"); cus = getenv("USER"); if (cus != NULL) setv(STRuser, quote(SAVE(cus)), VAR_READWRITE); else if (cln != NULL) setv(STRuser, quote(SAVE(cln)), VAR_READWRITE); else if ((pw = xgetpwuid(uid)) == NULL) setcopy(STRuser, STRunknown, VAR_READWRITE); else setcopy(STRuser, str2short(pw->pw_name), VAR_READWRITE); if (cln == NULL) tsetenv(STRLOGNAME, varval(STRuser)); if (cus == NULL) tsetenv(STRKUSER, varval(STRuser)); cgr = getenv("GROUP"); if (cgr != NULL) setv(STRgroup, quote(SAVE(cgr)), VAR_READWRITE); else if ((gr = xgetgrgid(gid)) == NULL) setcopy(STRgroup, STRunknown, VAR_READWRITE); else setcopy(STRgroup, str2short(gr->gr_name), VAR_READWRITE); if (cgr == NULL) tsetenv(STRKGROUP, varval(STRgroup)); } /* * HOST may be wrong, since rexd transports the entire environment on sun * 3.x Just set it again */ { char cbuff[MAXHOSTNAMELEN]; if (gethostname(cbuff, sizeof(cbuff)) >= 0) { cbuff[sizeof(cbuff) - 1] = '\0'; /* just in case */ tsetenv(STRHOST, str2short(cbuff)); } else tsetenv(STRHOST, STRunknown); } #ifdef REMOTEHOST /* * Try to determine the remote host we were logged in from. */ remotehost(); #endif /* REMOTEHOST */ #ifdef apollo if ((tcp = getenv("SYSTYPE")) == NULL) tcp = "bsd4.3"; tsetenv(STRSYSTYPE, quote(str2short(tcp))); #endif /* apollo */ /* * set editing on by default, unless running under Emacs as an inferior * shell. * We try to do this intelligently. If $TERM is available, then it * should determine if we should edit or not. $TERM is preserved * across rlogin sessions, so we will not get confused if we rlogin * under an emacs shell. Another advantage is that if we run an * xterm under an emacs shell, then the $TERM will be set to * xterm, so we are going to want to edit. Unfortunately emacs * does not restore all the tty modes, so xterm is not very well * set up. But this is not the shell's fault. * Also don't edit if $TERM == wm, for when we're running under an ATK app. * Finally, emacs compiled under terminfo, sets the terminal to dumb, * so disable editing for that too. * * Unfortunately, in some cases the initial $TERM setting is "unknown", * "dumb", or "network" which is then changed in the user's startup files. * We fix this by setting noediting here if $TERM is unknown/dumb and * if noediting is set, we switch on editing if $TERM is changed. */ if ((tcp = getenv("TERM")) != NULL) { setv(STRterm, quote(SAVE(tcp)), VAR_READWRITE); noediting = strcmp(tcp, "unknown") == 0 || strcmp(tcp, "dumb") == 0 || strcmp(tcp, "network") == 0; editing = strcmp(tcp, "emacs") != 0 && strcmp(tcp, "wm") != 0 && !noediting; } else { noediting = 0; editing = ((tcp = getenv("EMACS")) == NULL || strcmp(tcp, "t") != 0); } /* * The 'edit' variable is either set or unset. It doesn't * need a value. Making it 'emacs' might be confusing. */ if (editing) setNS(STRedit); /* * still more mutability: make the complete routine automatically add the * suffix of file names... */ setNS(STRaddsuffix); /* * Compatibility with tcsh >= 6.12 by default */ setNS(STRcsubstnonl); /* * Random default kill ring size */ setcopy(STRkillring, str2short("30"), VAR_READWRITE); /* * Re-initialize path if set in environment */ if ((tcp = getenv("PATH")) == NULL) #ifdef _PATH_DEFPATH importpath(str2short(_PATH_DEFPATH)); #else /* !_PATH_DEFPATH */ setq(STRpath, defaultpath(), &shvhed, VAR_READWRITE); #endif /* _PATH_DEFPATH */ else /* Importpath() allocates memory for the path, and the * returned pointer from SAVE() was discarded, so * this was a memory leak.. (sg) * * importpath(SAVE(tcp)); */ importpath(str2short(tcp)); { /* If the SHELL environment variable ends with "tcsh", set * STRshell to the same path. This is to facilitate using * the executable in environments where the compiled-in * default isn't appropriate (sg). */ size_t sh_len = 0; if ((tcp = getenv("SHELL")) != NULL) { sh_len = strlen(tcp); if ((sh_len >= 5 && strcmp(tcp + (sh_len - 5), "/tcsh") == 0) || (!tcsh && sh_len >= 4 && strcmp(tcp + (sh_len - 4), "/csh") == 0)) setv(STRshell, quote(SAVE(tcp)), VAR_READWRITE); else sh_len = 0; } if (sh_len == 0) setcopy(STRshell, STR_SHELLPATH, VAR_READWRITE); } #ifdef _OSD_POSIX /* BS2000 needs this variable set to "SHELL" */ if ((tcp = getenv("PROGRAM_ENVIRONMENT")) == NULL) tcp = "SHELL"; tsetenv(STRPROGRAM_ENVIRONMENT, quote(str2short(tcp))); #endif /* _OSD_POSIX */ #ifdef COLOR_LS_F if ((tcp = getenv("LS_COLORS")) != NULL) parseLS_COLORS(str2short(tcp)); if ((tcp = getenv("LSCOLORS")) != NULL) parseLSCOLORS(str2short(tcp)); #endif /* COLOR_LS_F */ mainpid = getpid(); doldol = putn((tcsh_number_t)mainpid); /* For $$ */ #ifdef WINNT_NATIVE { char *tmp; Char *tmp2; if ((tmp = getenv("TMP")) != NULL) { tmp = xasprintf("%s/%s", tmp, "sh"); tmp2 = SAVE(tmp); xfree(tmp); } else { tmp2 = SAVE(""); } shtemp = Strspl(tmp2, doldol); /* For << */ xfree(tmp2); } #else /* !WINNT_NATIVE */ #ifdef HAVE_MKSTEMP { const char *tmpdir = getenv ("TMPDIR"); if (!tmpdir) tmpdir = "/tmp"; shtemp = Strspl(SAVE(tmpdir), SAVE("/sh" TMP_TEMPLATE)); /* For << */ } #else /* !HAVE_MKSTEMP */ shtemp = Strspl(STRtmpsh, doldol); /* For << */ #endif /* HAVE_MKSTEMP */ #endif /* WINNT_NATIVE */ /* * 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. */ sigaction(SIGINT, NULL, &parintr); sigaction(SIGTERM, NULL, &parterm); #ifdef TCF /* Enable process migration on ourselves and our progeny */ (void) signal(SIGMIGRATE, SIG_DFL); #endif /* TCF */ /* * dspkanji/dspmbyte autosetting */ /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */ #if defined(DSPMBYTE) #if defined(NLS) && defined(LC_CTYPE) if (((tcp = setlocale(LC_CTYPE, NULL)) != NULL || (tcp = getenv("LANG")) != NULL) && !adrof(CHECK_MBYTEVAR)) #else if ((tcp = getenv("LANG")) != NULL && !adrof(CHECK_MBYTEVAR)) #endif { autoset_dspmbyte(str2short(tcp)); } #if defined(WINNT_NATIVE) else if (!adrof(CHECK_MBYTEVAR)) nt_autoset_dspmbyte(); #endif /* WINNT_NATIVE */ #endif #if defined(AUTOSET_KANJI) # if defined(NLS) && defined(LC_CTYPE) if (setlocale(LC_CTYPE, NULL) != NULL || getenv("LANG") != NULL) # else if (getenv("LANG") != NULL) # endif autoset_kanji(); #endif /* AUTOSET_KANJI */ fix_version(); /* publish the shell version */ if (argc > 1 && strcmp(argv[1], "--version") == 0) { xprintf("%S\n", varval(STRversion)); xexit(0); } if (argc > 1 && strcmp(argv[1], "--help") == 0) { xprintf("%S\n\n", varval(STRversion)); xprintf("%s", CGETS(11, 8, HELP_STRING)); xexit(0); } /* * Process the arguments. * * Note that processing of -v/-x is actually delayed till after script * processing. * * We set the first character of our name to be '-' if we are a shell * running interruptible commands. Many programs which examine ps'es * use this to filter such shells out. */ argc--, tempv++; while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) { do switch (*tcp++) { case 0: /* - Interruptible, no prompt */ prompt = 0; setintr = 1; nofile = 1; break; case 'b': /* -b Next arg is input file */ batch = 1; break; case 'c': /* -c Command input from arg */ if (argc == 1) xexit(0); argc--, tempv++; #ifdef M_XENIX /* Xenix Vi bug: it relies on a 7 bit environment (/bin/sh), so it pass ascii arguments with the 8th bit set */ if (!strcmp(argv[0], "sh")) { char *p; for (p = tempv[0]; *p; ++p) *p &= ASCII; } #endif - arginp = SAVE(tempv[0]); - - /* - * we put the command into a variable - */ - if (arginp != NULL) - setv(STRcommand, quote(Strsave(arginp)), VAR_READWRITE); - - /* - * * Give an error on -c arguments that end in * backslash to - * ensure that you don't make * nonportable csh scripts. - */ - { - int count; - - cp = Strend(arginp); - count = 0; - while (cp > arginp && *--cp == '\\') - ++count; - if ((count & 1) != 0) { - exiterr = 1; - stderror(ERR_ARGC); - } - } + targinp = tempv[0]; prompt = 0; nofile = 1; break; case 'd': /* -d Load directory stack from file */ rdirs = 1; break; #ifdef apollo case 'D': /* -D Define environment variable */ { Char *dp; cp = str2short(tcp); if (dp = Strchr(cp, '=')) { *dp++ = '\0'; tsetenv(cp, dp); } else tsetenv(cp, STRNULL); } *tcp = '\0'; /* done with this argument */ break; #endif /* apollo */ case 'e': /* -e Exit on any error */ exiterr = 1; break; case 'f': /* -f Fast start */ fast = 1; break; case 'i': /* -i Interactive, even if !intty */ intact = 1; nofile = 1; break; case 'm': /* -m read .cshrc (from su) */ mflag = 1; break; case 'n': /* -n Don't execute */ noexec = 1; break; case 'q': /* -q (Undoc'd) ... die on quit */ quitit = 1; break; case 's': /* -s Read from std input */ nofile = 1; break; case 't': /* -t Read one line from input */ onelflg = 2; prompt = 0; nofile = 1; break; 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(STRverbose); /* NOW! */ break; case 'X': /* -X Echo just before execution */ setNS(STRecho); /* NOW! */ break; case 'F': /* * This will cause children to be created using fork instead of * vfork. */ use_fork = 1; break; case ' ': case '\t': case '\r': case '\n': /* * for O/S's that don't do the argument parsing right in * "#!/foo -f " scripts */ break; default: /* Unknown command option */ exiterr = 1; stderror(ERR_TCSHUSAGE, tcp-1, progname); break; } while (*tcp); tempv++, argc--; } 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 (nofile == 0 && argc > 0) { nofile = xopen(tempv[0], O_RDONLY|O_LARGEFILE); if (nofile < 0) { child = 1; /* So this ... */ /* ... doesn't return */ stderror(ERR_SYSTEM, tempv[0], strerror(errno)); } xfree(ffile); dolzero = 1; ffile = SAVE(tempv[0]); /* * Replace FSHIN. Handle /dev/std{in,out,err} specially * since once they are closed we cannot open them again. * In that case we use our own saved descriptors */ if ((SHIN = dmove(nofile, FSHIN)) < 0) switch(nofile) { case 0: SHIN = FSHIN; break; case 1: SHIN = FSHOUT; break; case 2: SHIN = FSHDIAG; break; default: stderror(ERR_SYSTEM, tempv[0], strerror(errno)); break; } (void) close_on_exec(SHIN, 1); prompt = 0; /* argc not used any more */ tempv++; } /* * Call to closem() used to be part of initdesc(). Now called below where * the script name argument has become stdin. Kernel may have used a file * descriptor to hold the name of the script (setuid case) and this name * mustn't be lost by closing the fd too soon. */ closem(); /* * Consider input a tty if it really is or we are interactive. but not for * editing (christos) */ if (!(intty = isatty(SHIN))) { if (adrof(STRedit)) unsetv(STRedit); editing = 0; } intty |= intact; #ifndef convex if (intty || (intact && isatty(SHOUT))) { if (!batch && (uid != euid || gid != egid)) { errno = EACCES; child = 1; /* So this ... */ /* ... doesn't return */ stderror(ERR_SYSTEM, progname, strerror(errno)); } } #endif /* convex */ isoutatty = isatty(SHOUT); isdiagatty = isatty(SHDIAG); /* * 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; settell(); /* * Save the remaining arguments in argv. */ setq(STRargv, blk2short(tempv), &shvhed, VAR_READWRITE); /* * Set up the prompt. */ if (prompt) { setcopy(STRprompt, STRdefprompt, VAR_READWRITE); /* that's a meta-questionmark */ setcopy(STRprompt2, STRmquestion, VAR_READWRITE); setcopy(STRprompt3, STRKCORRECT, VAR_READWRITE); } /* * If we are an interactive shell, then start fiddling with the signals; * this is a tricky game. */ shpgrp = mygetpgrp(); opgrp = tpgrp = -1; if (setintr) { struct sigaction osig; **argv = '-'; if (!quitit) /* Wary! */ (void) signal(SIGQUIT, SIG_IGN); pintr_disabled = 1; sigset_interrupting(SIGINT, queue_pintr); (void) signal(SIGTERM, SIG_IGN); /* * No reason I can see not to save history on all these events.. * Most usual occurrence is in a window system, where we're not a login * shell, but might as well be... (sg) * But there might be races when lots of shells exit together... * [this is also incompatible]. * We have to be mre careful here. If the parent wants to * ignore the signals then we leave them untouched... * We also only setup the handlers for shells that are trully * interactive. */ sigaction(SIGHUP, NULL, &osig); if (loginsh || osig.sa_handler != SIG_IGN) /* exit processing on HUP */ sigset_interrupting(SIGHUP, queue_phup); #ifdef SIGXCPU sigaction(SIGXCPU, NULL, &osig); if (loginsh || osig.sa_handler != SIG_IGN) /* exit processing on XCPU */ sigset_interrupting(SIGXCPU, queue_phup); #endif #ifdef SIGXFSZ sigaction(SIGXFSZ, NULL, &osig); if (loginsh || osig.sa_handler != SIG_IGN) /* exit processing on XFSZ */ sigset_interrupting(SIGXFSZ, queue_phup); #endif - if (quitit == 0 && arginp == 0) { + if (quitit == 0 && targinp == 0) { #ifdef SIGTSTP (void) signal(SIGTSTP, SIG_IGN); #endif #ifdef SIGTTIN (void) signal(SIGTTIN, SIG_IGN); #endif #ifdef SIGTTOU (void) signal(SIGTTOU, SIG_IGN); #endif /* * 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; #ifdef NeXT /* NeXT 2.0 /usr/etc/rlogind, does not set our process group! */ if (f != -1 && shpgrp == 0) { shpgrp = getpid(); (void) setpgid(0, shpgrp); (void) tcsetpgrp(f, shpgrp); } #endif /* NeXT */ #ifdef BSDJOBS /* if we have tty job control */ if (f != -1 && grabpgrp(f, shpgrp) != -1) { /* * Thanks to Matt Day for the POSIX references, and to * Paul Close for the SGI clarification. */ if (setdisc(f) != -1) { opgrp = shpgrp; shpgrp = getpid(); tpgrp = shpgrp; if (tcsetpgrp(f, shpgrp) == -1) { /* * On hpux 7.03 this fails with EPERM. This happens on * the 800 when opgrp != shpgrp at this point. (we were * forked from a non job control shell) * POSIX 7.2.4, says we failed because the process * group specified did not belong to a process * in the same session with the tty. So we set our * process group and try again. */ if (setpgid(0, shpgrp) == -1) { xprintf("setpgid:"); goto notty; } if (tcsetpgrp(f, shpgrp) == -1) { xprintf("tcsetpgrp:"); goto notty; } } /* * We check the process group now. If it is the same, then * we don't need to set it again. On hpux 7.0 on the 300's * if we set it again it fails with EPERM. This is the * correct behavior according to POSIX 4.3.3 if the process * was a session leader . */ else if (shpgrp != mygetpgrp()) { if(setpgid(0, shpgrp) == -1) { xprintf("setpgid:"); goto notty; } } #ifdef IRIS4D /* * But on irix 3.3 we need to set it again, even if it is * the same. We do that to tell the system that we * need BSD process group compatibility. */ else (void) setpgid(0, shpgrp); #endif (void) close_on_exec(dcopy(f, FSHTTY), 1); } else tpgrp = -1; } if (tpgrp == -1) { notty: xprintf(CGETS(11, 1, "Warning: no access to tty (%s).\n"), strerror(errno)); xprintf("%s", CGETS(11, 2, "Thus no job control in this shell.\n")); /* * Fix from:Sakari Jalovaara if we don't * have access to tty, disable editing too */ if (adrof(STRedit)) unsetv(STRedit); editing = 0; } #else /* BSDJOBS */ /* don't have job control, so frotz it */ tpgrp = -1; #endif /* BSDJOBS */ } } if (setintr == 0 && parintr.sa_handler == SIG_DFL) setintr = 1; /* * SVR4 doesn't send a SIGCHLD when a child is stopped or continued if the * handler is installed with signal(2) or sigset(2). sigaction(2) must * be used instead. * * David Dawes (dawes@physics.su.oz.au) Sept 1991 */ sigset_interrupting(SIGCHLD, queue_pchild); - if (intty && !arginp) + if (intty && !targinp) (void) ed_Setup(editing);/* Get the tty state, and set defaults */ /* Only alter the tty state if editing */ /* * Set an exit here in case of an interrupt or error reading the shell * start-up scripts. */ osetintr = setintr; oparintr = parintr; (void)cleanup_push_mark(); /* There is no outer handler */ if (setexit() != 0) /* PWP */ reenter = 1; else reenter = 0; exitset++; haderr = 0; /* In case second time through */ if (!fast && reenter == 0) { /* Will have varval(STRhome) here because set fast if don't */ { pintr_disabled++; cleanup_push(&pintr_disabled, disabled_cleanup); setintr = 0;/*FIXRESET:cleanup*/ /* onintr in /etc/ files has no effect */ parintr.sa_handler = SIG_IGN;/*FIXRESET: cleanup*/ #ifdef LOGINFIRST #ifdef _PATH_DOTLOGIN if (loginsh) (void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL); #endif #endif #ifdef _PATH_DOTCSHRC (void) srcfile(_PATH_DOTCSHRC, 0, 0, NULL); #endif - if (!arginp && !onelflg && !havhash) + if (!targinp && !onelflg && !havhash) dohash(NULL,NULL); #ifndef LOGINFIRST #ifdef _PATH_DOTLOGIN if (loginsh) (void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL); #endif #endif cleanup_until(&pintr_disabled); setintr = osetintr; parintr = oparintr; } #ifdef LOGINFIRST if (loginsh) (void) srccat(varval(STRhome), STRsldotlogin); #endif /* upward compat. */ if (!srccat(varval(STRhome), STRsldottcshrc)) (void) srccat(varval(STRhome), STRsldotcshrc); - if (!arginp && !onelflg && !havhash) + if (!targinp && !onelflg && !havhash) dohash(NULL,NULL); /* * Source history before .login so that it is available in .login */ loadhist(NULL, 0); #ifndef LOGINFIRST if (loginsh) (void) srccat(varval(STRhome), STRsldotlogin); #endif if (loginsh || rdirs) loaddirs(NULL); } /* Reset interrupt flag */ setintr = osetintr; parintr = oparintr; exitset--; /* Initing AFTER .cshrc is the Right Way */ - if (intty && !arginp) { /* PWP setup stuff */ + if (intty && !targinp) { /* PWP setup stuff */ ed_Init(); /* init the new line editor */ #ifdef SIG_WINDOW check_window_size(1); /* mung environment */ #endif /* SIG_WINDOW */ } /* * Now are ready for the -v and -x flags */ if (nverbose) setNS(STRverbose); if (nexececho) setNS(STRecho); + + if (targinp) { + arginp = SAVE(targinp); + /* + * we put the command into a variable + */ + if (arginp != NULL) + setv(STRcommand, quote(Strsave(arginp)), VAR_READWRITE); + + /* + * * Give an error on -c arguments that end in * backslash to + * ensure that you don't make * nonportable csh scripts. + */ + { + int count; + + cp = Strend(arginp); + count = 0; + while (cp > arginp && *--cp == '\\') + ++count; + if ((count & 1) != 0) { + exiterr = 1; + stderror(ERR_ARGC); + } + } + } /* * 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. */ /* Take care of these (especially HUP) here instead of inside flush. */ handle_pending_signals(); if (intty) { if (loginsh) { xprintf("logout\n"); xclose(SHIN); child = 1; #ifdef TESLA do_logout = 1; #endif /* TESLA */ goodbye(NULL, NULL); } else { xprintf("exit\n"); } } record(); exitstat(); return (0); } void untty(void) { #ifdef BSDJOBS if (tpgrp > 0 && opgrp != shpgrp) { (void) setpgid(0, opgrp); (void) tcsetpgrp(FSHTTY, opgrp); (void) resetdisc(FSHTTY); } #endif /* BSDJOBS */ } void importpath(Char *cp) { size_t i = 0; Char *dp; Char **pv; int c; for (dp = cp; *dp; dp++) if (*dp == PATHSEP) 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 = xcalloc(i + 2, sizeof(Char *)); dp = cp; i = 0; if (*dp) for (;;) { if ((c = *dp) == PATHSEP || c == 0) { *dp = 0; pv[i++] = Strsave(*cp ? cp : STRdot); if (c) { cp = dp + 1; *dp = PATHSEP; } else break; } #ifdef WINNT_NATIVE else if (*dp == '\\') *dp = '/'; #endif /* WINNT_NATIVE */ dp++; } pv[i] = 0; cleanup_push(pv, blk_cleanup); setq(STRpath, pv, &shvhed, VAR_READWRITE); cleanup_ignore(pv); cleanup_until(pv); } /* * Source to the file which is the catenation of the argument names. */ static int srccat(Char *cp, Char *dp) { if (cp[0] == '/' && cp[1] == '\0') return srcfile(short2str(dp), (mflag ? 0 : 1), 0, NULL); else { Char *ep; char *ptr; int rv; #ifdef WINNT_NATIVE ep = Strend(cp); if (ep != cp && ep[-1] == '/' && dp[0] == '/') /* silly win95 */ dp++; #endif /* WINNT_NATIVE */ ep = Strspl(cp, dp); cleanup_push(ep, xfree); ptr = short2str(ep); rv = srcfile(ptr, (mflag ? 0 : 1), 0, NULL); cleanup_until(ep); return rv; } } /* * Source to a file putting the file descriptor in a safe place (> 2). */ #ifndef WINNT_NATIVE static int #else int #endif /*WINNT_NATIVE*/ srcfile(const char *f, int onlyown, int flag, Char **av) { int unit; if ((unit = xopen(f, O_RDONLY|O_LARGEFILE)) == -1) return 0; cleanup_push(&unit, open_cleanup); unit = dmove(unit, -1); cleanup_ignore(&unit); cleanup_until(&unit); (void) close_on_exec(unit, 1); srcunit(unit, onlyown, flag, av); return 1; } /* * Save the shell state, and establish new argument vector, and new input * fd. */ static void st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av) { st->insource = insource; st->SHIN = SHIN; /* Want to preserve the meaning of "source file >output". * Save old descriptors, move new 0,1,2 to safe places and assign * them to SH* and let process() redo 0,1,2 from them. * * The macro returns true if d1 and d2 are good and they point to * different things. If you don't avoid saving duplicate * descriptors, you really limit the depth of "source" recursion * you can do because of all the open file descriptors. -IAN! */ #define NEED_SAVE_FD(d1,d2) \ (fstat(d1, &s1) != -1 && fstat(d2, &s2) != -1 \ && (s1.st_ino != s2.st_ino || s1.st_dev != s2.st_dev) ) st->OLDSTD = st->SHOUT = st->SHDIAG = -1;/* test later to restore these */ if (didfds) { struct stat s1, s2; if (NEED_SAVE_FD(0,OLDSTD)) { st->OLDSTD = OLDSTD; OLDSTD = dmove(0, -1); (void)close_on_exec(OLDSTD, 1); } if (NEED_SAVE_FD(1,SHOUT)) { st->SHOUT = SHOUT; SHOUT = dmove(1, -1); (void)close_on_exec(SHOUT, 1); } if (NEED_SAVE_FD(2,SHDIAG)) { st->SHDIAG = SHDIAG; SHDIAG = dmove(2, -1); (void)close_on_exec(SHDIAG, 1); } donefds(); } st->intty = intty; st->whyles = whyles; st->gointr = gointr; st->arginp = arginp; st->evalp = evalp; st->evalvec = evalvec; st->alvecp = alvecp; st->alvec = alvec; st->onelflg = onelflg; st->enterhist = enterhist; st->justpr = justpr; if (hflg) st->HIST = HIST; else st->HIST = '\0'; st->cantell = cantell; cpybin(st->B, B); /* * we can now pass arguments to source. * For compatibility we do that only if arguments were really * passed, otherwise we keep the old, global $argv like before. */ if (av != NULL && *av != NULL) { struct varent *vp; if ((vp = adrof(STRargv)) != NULL && vp->vec != NULL) st->argv = saveblk(vp->vec); else st->argv = NULL; setq(STRargv, saveblk(av), &shvhed, VAR_READWRITE); } else st->argv = NULL; st->av = av; SHIN = unit; /* Do this first */ /* Establish new input arena */ { fbuf = NULL; fseekp = feobp = fblocks = 0; settell(); } arginp = 0; onelflg = 0; intty = isatty(SHIN); whyles = 0; gointr = 0; evalvec = 0; evalp = 0; alvec = al; alvecp = 0; enterhist = hflg; if (enterhist) HIST = '\0'; insource = 1; } /* * Restore the shell to a saved state */ static void st_restore(void *xst) { struct saved_state *st; st = xst; if (st->SHIN == -1) return; /* Reset input arena */ { int i; Char** nfbuf = fbuf; int nfblocks = fblocks; fblocks = 0; fbuf = NULL; for (i = 0; i < nfblocks; i++) xfree(nfbuf[i]); xfree(nfbuf); } cpybin(B, st->B); xclose(SHIN); insource = st->insource; SHIN = st->SHIN; if (st->OLDSTD != -1) xclose(OLDSTD), OLDSTD = st->OLDSTD; if (st->SHOUT != -1) xclose(SHOUT), SHOUT = st->SHOUT; if (st->SHDIAG != -1) xclose(SHDIAG), SHDIAG = st->SHDIAG; arginp = st->arginp; onelflg = st->onelflg; evalp = st->evalp; evalvec = st->evalvec; alvecp = st->alvecp; alvec = st->alvec; intty = st->intty; whyles = st->whyles; gointr = st->gointr; if (st->HIST != '\0') HIST = st->HIST; enterhist = st->enterhist; cantell = st->cantell; justpr = st->justpr; if (st->argv != NULL) setq(STRargv, st->argv, &shvhed, VAR_READWRITE); else if (st->av != NULL && *st->av != NULL && adrof(STRargv) != NULL) unsetv(STRargv); } /* * 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. */ static void srcunit(int unit, int onlyown, int hflg, Char **av) { struct saved_state st; st.SHIN = -1; /* st_restore checks this */ if (unit < 0) return; if (onlyown) { struct stat stb; if (fstat(unit, &stb) < 0) { xclose(unit); return; } } /* Does nothing before st_save() because st.SHIN == -1 */ cleanup_push(&st, st_restore); if (setintr) { pintr_disabled++; cleanup_push(&pintr_disabled, disabled_cleanup); } /* Save the current state and move us to a new state */ st_save(&st, unit, hflg, NULL, av); /* * Now if we are allowing commands to be interrupted, we let ourselves be * interrupted. */ if (setintr) { cleanup_until(&pintr_disabled); pintr_disabled++; cleanup_push(&pintr_disabled, disabled_cleanup); } process(0); /* 0 -> blow away on errors */ /* Restore the old state */ cleanup_until(&st); } /*ARGSUSED*/ void goodbye(Char **v, struct command *c) { USE(v); USE(c); record(); if (loginsh) { size_t omark; sigset_t set; sigemptyset(&set); signal(SIGQUIT, SIG_IGN); sigaddset(&set, SIGQUIT); sigprocmask(SIG_UNBLOCK, &set, NULL); signal(SIGINT, SIG_IGN); sigaddset(&set, SIGINT); signal(SIGTERM, SIG_IGN); sigaddset(&set, SIGTERM); signal(SIGHUP, SIG_IGN); sigaddset(&set, SIGHUP); sigprocmask(SIG_UNBLOCK, &set, NULL); phup_disabled = 1; setintr = 0; /* No interrupts after "logout" */ /* Trap errors inside .logout */ omark = cleanup_push_mark(); if (setexit() == 0) { if (!(adrof(STRlogout))) setcopy(STRlogout, STRnormal, VAR_READWRITE); #ifdef _PATH_DOTLOGOUT (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL); #endif if (adrof(STRhome)) (void) srccat(varval(STRhome), STRsldtlogout); #ifdef TESLA do_logout = 1; #endif /* TESLA */ } cleanup_pop_mark(omark); } exitstat(); } void exitstat(void) { #ifdef PROF _mcleanup(); #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 = 1; xexit(getn(varval(STRstatus))); } /* * in the event of a HUP we want to save the history */ void phup(void) { if (loginsh) { setcopy(STRlogout, STRhangup, VAR_READWRITE); #ifdef _PATH_DOTLOGOUT (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL); #endif if (adrof(STRhome)) (void) srccat(varval(STRhome), STRsldtlogout); } record(); #ifdef POSIXJOBS /* * We kill the last foreground process group. It then becomes * responsible to propagate the SIGHUP to its progeny. */ { struct process *pp, *np; for (pp = proclist.p_next; pp; pp = pp->p_next) { np = pp; /* * Find if this job is in the foreground. It could be that * the process leader has exited and the foreground flag * is cleared for it. */ do /* * If a process is in the foreground we try to kill * it's process group. If we succeed, then the * whole job is gone. Otherwise we keep going... * But avoid sending HUP to the shell again. */ if (((np->p_flags & PFOREGND) != 0) && np->p_jobid != shpgrp) { np->p_flags &= ~PHUP; if (killpg(np->p_jobid, SIGHUP) != -1) { /* In case the job was suspended... */ #ifdef SIGCONT (void) killpg(np->p_jobid, SIGCONT); #endif break; } } while ((np = np->p_friends) != pp); } } #endif /* POSIXJOBS */ xexit(SIGHUP); } static Char *jobargv[2] = {STRjobs, 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. */ int just_signaled; /* bugfix by Michael Bloom (mg@ttidca.TTI.COM) */ void pintr(void) { just_signaled = 1; pintr1(1); } void pintr1(int wantnl) { if (setintr) { if (pjobs) { pjobs = 0; xputchar('\n'); dojobs(jobargv, NULL); stderror(ERR_NAME | ERR_INTR); } } /* MH - handle interrupted completions specially */ { if (InsideCompletion) stderror(ERR_SILENT); } /* JV - Make sure we shut off inputl */ { (void) Cookedmode(); GettingInput = 0; if (evalvec) doneinp = 1; } drainoline(); #ifdef HAVE_GETPWENT (void) endpwent(); #endif /* * 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) { gotolab(gointr); reset(); } else if (intty && wantnl) { if (editing) { /* * If we are editing a multi-line input command, and move to * the beginning of the line, we don't want to trash it when * we hit ^C */ PastBottom(); ClearLines(); ClearDisp(); } else { /* xputchar('\n'); *//* Some like this, others don't */ (void) putraw('\r'); (void) putraw('\n'); } } stderror(ERR_SILENT); } /* * 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(int catch) { jmp_buf_t osetexit; /* PWP: This might get nuked by longjmp so don't make it a register var */ size_t omark; volatile int didexitset = 0; getexit(osetexit); omark = cleanup_push_mark(); for (;;) { struct command *t; int hadhist, old_pintr_disabled; (void)setexit(); if (didexitset == 0) { exitset++; didexitset++; } pendjob(); justpr = enterhist; /* execute if not entering history */ if (haderr) { if (!catch) { /* unwind */ doneinp = 0; cleanup_pop_mark(omark); 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) { just_signaled = 0; mailchk(); /* * Watch for logins/logouts. Next is scheduled commands stored * previously using "sched." Then execute periodic commands. * Following that, the prompt precmd is run. */ #ifndef HAVENOUTMP watch_login(0); #endif /* !HAVENOUTMP */ sched_run(); period_cmd(); precmd(); /* * 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 && aret == TCSH_F_SEEK) printprompt(0, NULL); flush(); setalarm(1); } if (seterr) { xfree(seterr); seterr = NULL; } /* * Interruptible during interactive reads */ if (setintr) pintr_push_enable(&old_pintr_disabled); freelex(¶ml); hadhist = lex(¶ml); if (setintr) cleanup_until(&old_pintr_disabled); cleanup_push(¶ml, lex_cleanup); /* * Echo not only on VERBOSE, but also with history expansion. If there * is a lexical error then we forego history echo. * Do not echo if we're only entering history (source -h). */ if ((hadhist && !seterr && intty && !tellwhat && !Expand && !whyles) || (!enterhist && adrof(STRverbose))) { int odidfds = didfds; haderr = 1; didfds = 0; prlex(¶ml); flush(); haderr = 0; didfds = odidfds; } (void) alarm(0); /* Autologout OFF */ alrmcatch_disabled = 1; /* * 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. * * PWP: entry of items in the history list while in a while loop is done * elsewhere... */ if (enterhist || (catch && intty && !whyles && !tellwhat && !arun)) savehist(¶ml, enterhist > 1); if (Expand && seterr) Expand = 0; /* * Print lexical error messages, except when sourcing history lists. */ if (!enterhist && seterr) stderror(ERR_OLD); /* * If had a history command :p modifier then this is as far as we * should go */ if (justpr) goto cmd_done; /* * If had a tellwhat from twenex() then do */ if (tellwhat) { (void) tellmewhat(¶ml, NULL); goto cmd_done; } alias(¶ml); #ifdef BSDJOBS /* * If we are interactive, try to continue jobs that we have stopped */ if (prompt) continue_jobs(¶ml); #endif /* BSDJOBS */ /* * Check to see if the user typed "rm * .o" or something */ if (prompt) rmstar(¶ml); /* * Parse the words of the input into a parse tree. */ t = syntax(paraml.next, ¶ml, 0); /* * We cannot cleanup push here, because cd /blah; echo foo * would rewind t on the chdir error, and free the rest of the command */ if (seterr) { freesyn(t); stderror(ERR_OLD); } postcmd(); /* * Execute the parse tree From: Michael Schroeder * was execute(t, tpgrp); */ execute(t, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE); freesyn(t); /* * Made it! */ #ifdef SIG_WINDOW if (windowchg || (catch && intty && !whyles && !tellwhat)) { (void) check_window_size(0); /* for window systems */ } #endif /* SIG_WINDOW */ setcopy(STR_, InputBuf, VAR_READWRITE | VAR_NOGLOB); cmd_done: if (cleanup_reset()) cleanup_until(¶ml); else haderr = 1; } cleanup_pop_mark(omark); resexit(osetexit); exitset--; handle_pending_signals(); } /*ARGSUSED*/ void dosource(Char **t, struct command *c) { Char *f; int hflg = 0; char *file; USE(c); t++; if (*t && eq(*t, STRmh)) { if (*++t == NULL) stderror(ERR_NAME | ERR_HFLAG); hflg++; } else if (*t && eq(*t, STRmm)) { if (*++t == NULL) stderror(ERR_NAME | ERR_MFLAG); hflg = 2; } f = globone(*t++, G_ERROR); file = strsave(short2str(f)); cleanup_push(file, xfree); xfree(f); t = glob_all_or_error(t); cleanup_push(t, blk_cleanup); if ((!srcfile(file, 0, hflg, t)) && (!hflg) && (!bequiet)) stderror(ERR_SYSTEM, file, strerror(errno)); cleanup_until(file); } /* * 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 he already * knows, since the login program insists on saying * "You have mail." */ /* * The AMS version. * This version checks if the file is a directory, and if so, * tells you the number of files in it, otherwise do the old thang. * The magic "+1" in the time calculation is to compensate for * an AFS bug where directory mtimes are set to 1 second in * the future. */ static void mailchk(void) { struct varent *v; Char **vp; time_t t; int intvl, cnt; struct stat stb; int new; v = adrof(STRmail); if (v == NULL || v->vec == NULL) 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++) { char *filename = short2str(*vp); char *mboxdir = filename; if (stat(filename, &stb) < 0) continue; #if defined(BSDTIMES) || defined(_SEQUENT_) new = stb.st_mtime > time0.tv_sec; #else new = stb.st_mtime > seconds0; #endif if (S_ISDIR(stb.st_mode)) { DIR *mailbox; int mailcount = 0; char *tempfilename; struct stat stc; tempfilename = xasprintf("%s/new", filename); if (stat(tempfilename, &stc) != -1 && S_ISDIR(stc.st_mode)) { /* * "filename/new" exists and is a directory; you are * using Qmail. */ stb = stc; #if defined(BSDTIMES) || defined(_SEQUENT_) new = stb.st_mtime > time0.tv_sec; #else new = stb.st_mtime > seconds0; #endif mboxdir = tempfilename; } if (stb.st_mtime <= chktim + 1 || (loginsh && !new)) { xfree(tempfilename); continue; } mailbox = opendir(mboxdir); xfree(tempfilename); if (mailbox == NULL) continue; /* skip . and .. */ if (!readdir(mailbox) || !readdir(mailbox)) { (void)closedir(mailbox); continue; } while (readdir(mailbox)) mailcount++; (void)closedir(mailbox); if (mailcount == 0) continue; if (cnt == 1) xprintf(CGETS(11, 3, "You have %d mail messages.\n"), mailcount); else xprintf(CGETS(11, 4, "You have %d mail messages in %s.\n"), mailcount, filename); } else { char *type; if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime || (stb.st_atime <= chktim && stb.st_mtime <= chktim) || (loginsh && !new)) continue; type = strsave(new ? CGETS(11, 6, "new ") : ""); cleanup_push(type, xfree); if (cnt == 1) xprintf(CGETS(11, 5, "You have %smail.\n"), type); else xprintf(CGETS(11, 7, "You have %smail in %s.\n"), type, filename); cleanup_until(type); } } 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 return home directory of the user, or NULL. */ Char * gethdir(const Char *home) { Char *h; /* * Is it us? */ if (*home == '\0') { if ((h = varval(STRhome)) != STRNULL) return Strsave(h); else return NULL; } /* * Look in the cache */ if ((h = gettilde(home)) == NULL) return NULL; else return Strsave(h); } /* * Move the initial descriptors to their eventual * resting places, closing all other units. */ void initdesc(void) { #ifdef NLS_BUGS #ifdef NLS_CATALOGS nlsclose(); #endif /* NLS_CATALOGS */ #endif /* NLS_BUGS */ didfds = 0; /* 0, 1, 2 aren't set up */ (void) close_on_exec(SHIN = dcopy(0, FSHIN), 1); (void) close_on_exec(SHOUT = dcopy(1, FSHOUT), 1); (void) close_on_exec(SHDIAG = dcopy(2, FSHDIAG), 1); (void) close_on_exec(OLDSTD = dcopy(SHIN, FOLDSTD), 1); #ifndef CLOSE_ON_EXEC didcch = 0; /* Havent closed for child */ #endif /* CLOSE_ON_EXEC */ if (SHDIAG >= 0) isdiagatty = isatty(SHDIAG); else isdiagatty = 0; if (SHDIAG >= 0) isoutatty = isatty(SHOUT); else isoutatty = 0; #ifdef NLS_BUGS #ifdef NLS_CATALOGS nlsinit(); #endif /* NLS_CATALOGS */ #endif /* NLS_BUGS */ } void #ifdef PROF done(int i) #else xexit(int i) #endif { #ifdef TESLA if (loginsh && do_logout) { /* this is to send hangup signal to the develcon */ /* we toggle DTR. clear dtr - sleep 1 - set dtr */ /* ioctl will return ENOTTY for pty's but we ignore it */ /* exitstat will run after disconnect */ /* we sleep for 2 seconds to let things happen in */ /* .logout and rechist() */ #ifdef TIOCCDTR (void) sleep(2); (void) ioctl(FSHTTY, TIOCCDTR, NULL); (void) sleep(1); (void) ioctl(FSHTTY, TIOCSDTR, NULL); #endif /* TIOCCDTR */ } #endif /* TESLA */ { struct process *pp, *np; pid_t mypid = getpid(); /* Kill all processes marked for hup'ing */ for (pp = proclist.p_next; pp; pp = pp->p_next) { np = pp; do if ((np->p_flags & PHUP) && np->p_jobid != shpgrp && np->p_parentid == mypid) { if (killpg(np->p_jobid, SIGHUP) != -1) { /* In case the job was suspended... */ #ifdef SIGCONT (void) killpg(np->p_jobid, SIGCONT); #endif break; } } while ((np = np->p_friends) != pp); } } untty(); #ifdef NLS_CATALOGS /* * We need to call catclose, because SVR4 leaves symlinks behind otherwise * in the catalog directories. We cannot close on a vforked() child, * because messages will stop working on the parent too. */ if (child == 0) nlsclose(); #endif /* NLS_CATALOGS */ #ifdef WINNT_NATIVE nt_cleanup(); #endif /* WINNT_NATIVE */ _exit(i); } #ifndef _PATH_DEFPATH static Char ** defaultpath(void) { char *ptr; Char **blk, **blkp; struct stat stb; blkp = blk = xmalloc(sizeof(Char *) * 10); #ifndef NODOT # ifndef DOTLAST *blkp++ = Strsave(STRdot); # endif #endif #define DIRAPPEND(a) \ if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \ *blkp++ = SAVE(ptr) #ifdef _PATH_LOCAL DIRAPPEND(_PATH_LOCAL); #endif #ifdef _PATH_USRUCB DIRAPPEND(_PATH_USRUCB); #endif #ifdef _PATH_USRBSD DIRAPPEND(_PATH_USRBSD); #endif #ifdef _PATH_BIN DIRAPPEND(_PATH_BIN); #endif #ifdef _PATH_USRBIN DIRAPPEND(_PATH_USRBIN); #endif #undef DIRAPPEND #ifndef NODOT # ifdef DOTLAST *blkp++ = Strsave(STRdot); # endif #endif *blkp = NULL; return (blk); } #endif static void record(void) { if (!fast) { recdirs(NULL, adrof(STRsavedirs) != NULL); rechist(NULL, adrof(STRsavehist) != NULL); } displayHistStats("Exiting"); /* no-op unless DEBUG_HIST */ } /* * Grab the tty repeatedly, and give up if we are not in the correct * tty process group. */ int grabpgrp(int fd, pid_t desired) { struct sigaction old; pid_t pgrp; size_t i; for (i = 0; i < 100; i++) { if ((pgrp = tcgetpgrp(fd)) == -1) return -1; if (pgrp == desired) return 0; (void)sigaction(SIGTTIN, NULL, &old); (void)signal(SIGTTIN, SIG_DFL); (void)kill(0, SIGTTIN); (void)sigaction(SIGTTIN, &old, NULL); } errno = EPERM; return -1; } Index: head/contrib/tcsh/sh.func.c =================================================================== --- head/contrib/tcsh/sh.func.c (revision 315951) +++ head/contrib/tcsh/sh.func.c (revision 315952) @@ -1,2749 +1,2751 @@ /* $Header: /p/tcsh/cvsroot/tcsh/sh.func.c,v 3.176 2016/10/18 17:26:42 christos Exp $ */ /* * sh.func.c: csh builtin functions */ /*- * Copyright (c) 1980, 1991 The 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. 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 REGENTS 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. */ #include "sh.h" RCSID("$tcsh: sh.func.c,v 3.176 2016/10/18 17:26:42 christos Exp $") #include "ed.h" #include "tw.h" #include "tc.h" #ifdef WINNT_NATIVE #include "nt.const.h" #endif /* WINNT_NATIVE */ #if defined (NLS_CATALOGS) && defined(HAVE_ICONV) static iconv_t catgets_iconv; /* Or (iconv_t)-1 */ #endif /* * C shell */ extern int MapsAreInited; extern int NLSMapsAreInited; extern int GotTermCaps; static int zlast = -1; static void islogin (void); static void preread (void); static void doagain (void); static const char *isrchx (int); static void search (int, int, Char *); static int getword (struct Strbuf *); static struct wordent *histgetword (struct wordent *); static void toend (void); static void xecho (int, Char **); static int islocale_var (Char *); static void wpfree (struct whyle *); const struct biltins * isbfunc(struct command *t) { Char *cp = t->t_dcom[0]; const struct biltins *bp, *bp1, *bp2; static struct biltins label = {"", dozip, 0, 0}; static struct biltins foregnd = {"%job", dofg1, 0, 0}; static struct biltins backgnd = {"%job &", dobg1, 0, 0}; /* * We never match a builtin that has quoted the first * character; this has been the traditional way to escape * builtin commands. */ if (*cp & QUOTE) return NULL; if (*cp != ':' && lastchr(cp) == ':') { label.bname = short2str(cp); return (&label); } if (*cp == '%') { if (t->t_dflg & F_AMPERSAND) { t->t_dflg &= ~F_AMPERSAND; backgnd.bname = short2str(cp); return (&backgnd); } foregnd.bname = short2str(cp); return (&foregnd); } #ifdef WARP /* * This is a perhaps kludgy way to determine if the warp builtin is to be * acknowledged or not. If checkwarp() fails, then we are to assume that * the warp command is invalid, and carry on as we would handle any other * non-builtin command. -- JDK 2/4/88 */ if (eq(STRwarp, cp) && !checkwarp()) { return (0); /* this builtin disabled */ } #endif /* WARP */ /* * 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 = ((char) *cp) - *bp->bname) == 0 && (i = StrQcmp(cp, str2short(bp->bname))) == 0) return bp; if (i < 0) bp2 = bp; else bp1 = bp + 1; } #ifdef WINNT_NATIVE return nt_check_additional_builtins(cp); #endif /*WINNT_NATIVE*/ return (0); } void func(struct command *t, const struct biltins *bp) { int i; xechoit(t->t_dcom); setname(bp->bname); i = blklen(t->t_dcom) - 1; if (i < bp->minargs) stderror(ERR_NAME | ERR_TOOFEW); if (i > bp->maxargs) stderror(ERR_NAME | ERR_TOOMANY); (*bp->bfunct) (t->t_dcom, t); } /*ARGSUSED*/ void doonintr(Char **v, struct command *c) { Char *cp; Char *vv = v[1]; USE(c); if (parintr.sa_handler == SIG_IGN) return; if (setintr && intty) stderror(ERR_NAME | ERR_TERMINAL); cp = gointr; gointr = 0; xfree(cp); if (vv == 0) { if (setintr) sigset_interrupting(SIGINT, queue_pintr); else (void) signal(SIGINT, SIG_DFL); gointr = 0; } else if (eq((vv = strip(vv)), STRminus)) { (void) signal(SIGINT, SIG_IGN); gointr = Strsave(STRminus); } else { gointr = Strsave(vv); sigset_interrupting(SIGINT, queue_pintr); } } /*ARGSUSED*/ void donohup(Char **v, struct command *c) { USE(c); USE(v); if (intty) stderror(ERR_NAME | ERR_TERMINAL); if (setintr == 0) { (void) signal(SIGHUP, SIG_IGN); phup_disabled = 1; #ifdef CC submit(getpid()); #endif /* CC */ } } /*ARGSUSED*/ void dohup(Char **v, struct command *c) { USE(c); USE(v); if (intty) stderror(ERR_NAME | ERR_TERMINAL); if (setintr == 0) (void) signal(SIGHUP, SIG_DFL); } /*ARGSUSED*/ void dozip(Char **v, struct command *c) { USE(c); USE(v); } /*ARGSUSED*/ void dofiletest(Char **v, struct command *c) { Char **globbed, **fileptr, *ftest, *res; USE(c); if (*(ftest = *++v) != '-') stderror(ERR_NAME | ERR_FILEINQ); ++v; v = glob_all_or_error(v); globbed = v; cleanup_push(globbed, blk_cleanup); while (*(fileptr = v++) != '\0') { res = filetest(ftest, &fileptr, 0); cleanup_push(res, xfree); xprintf("%S", res); cleanup_until(res); if (*v) xprintf(" "); } xprintf("\n"); cleanup_until(globbed); } void prvars(void) { plist(&shvhed, VAR_ALL); } /*ARGSUSED*/ void doalias(Char **v, struct command *c) { struct varent *vp; Char *p; USE(c); v++; p = *v++; if (p == 0) plist(&aliases, VAR_ALL); else if (*v == 0) { vp = adrof1(strip(p), &aliases); if (vp && vp->vec) blkpr(vp->vec), xputchar('\n'); } else { if (eq(p, STRalias) || eq(p, STRunalias)) { setname(short2str(p)); stderror(ERR_NAME | ERR_DANGER); } set1(strip(p), saveblk(v), &aliases, VAR_READWRITE); tw_cmd_free(); } } /*ARGSUSED*/ void unalias(Char **v, struct command *c) { USE(c); unset1(v, &aliases); tw_cmd_free(); } /*ARGSUSED*/ void dologout(Char **v, struct command *c) { USE(c); USE(v); islogin(); goodbye(NULL, NULL); } /*ARGSUSED*/ void dologin(Char **v, struct command *c) { #ifdef WINNT_NATIVE USE(c); USE(v); #else /* !WINNT_NATIVE */ char **p = short2blk(v); USE(c); cleanup_push((Char **)p, blk_cleanup); islogin(); rechist(NULL, adrof(STRsavehist) != NULL); sigaction(SIGTERM, &parterm, NULL); (void) execv(_PATH_BIN_LOGIN, p); (void) execv(_PATH_USRBIN_LOGIN, p); cleanup_until((Char **)p); untty(); xexit(1); #endif /* !WINNT_NATIVE */ } #ifdef NEWGRP /*ARGSUSED*/ void donewgrp(Char **v, struct command *c) { char **p; if (chkstop == 0 && setintr) panystop(0); sigaction(SIGTERM, &parterm, NULL); p = short2blk(v); /* * From Beto Appleton (beto@aixwiz.austin.ibm.com) * Newgrp can take 2 arguments... */ (void) execv(_PATH_BIN_NEWGRP, p); (void) execv(_PATH_USRBIN_NEWGRP, p); blkfree((Char **) p); untty(); xexit(1); } #endif /* NEWGRP */ static void islogin(void) { if (chkstop == 0 && setintr) panystop(0); if (loginsh) return; stderror(ERR_NOTLOGIN); } void doif(Char **v, struct command *kp) { int i; Char **vv; v++; i = noexec ? 1 : expr(&v); vv = v; if (*vv == NULL) stderror(ERR_NAME | ERR_EMPTYIF); if (eq(*vv, STRthen)) { if (*++vv) stderror(ERR_NAME | ERR_IMPRTHEN); setname(short2str(STRthen)); /* * If expression was zero, then scan to else , otherwise just fall into * following code. */ if (!i) search(TC_IF, 0, NULL); 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) { kp->t_dflg &= F_SAVE; kp->t_dflg |= F_REPEAT; /* * 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), NULL, NULL, TRUE); } /*ARGSUSED*/ void doelse (Char **v, struct command *c) { USE(c); USE(v); if (!noexec) search(TC_ELSE, 0, NULL); } /*ARGSUSED*/ void dogoto(Char **v, struct command *c) { Char *lp; USE(c); lp = globone(v[1], G_ERROR); cleanup_push(lp, xfree); if (!noexec) gotolab(lp); cleanup_until(lp); } void gotolab(Char *lab) { struct whyle *wp; /* * 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. */ zlast = TC_GOTO; for (wp = whyles; wp; wp = wp->w_next) if (wp->w_end.type == TCSH_F_SEEK && wp->w_end.f_seek == 0) { search(TC_BREAK, 0, NULL); btell(&wp->w_end); } else { bseek(&wp->w_end); } search(TC_GOTO, 0, lab); /* * Eliminate loops which were exited. */ wfree(); } /*ARGSUSED*/ void doswitch(Char **v, struct command *c) { Char *cp, *lp; USE(c); v++; if (!*v || *(*v++) != '(') stderror(ERR_SYNTAX); cp = **v == ')' ? STRNULL : *v++; if (*(*v++) != ')') v--; if (*v) stderror(ERR_SYNTAX); lp = globone(cp, G_ERROR); cleanup_push(lp, xfree); if (!noexec) search(TC_SWITCH, 0, lp); cleanup_until(lp); } /*ARGSUSED*/ void dobreak(Char **v, struct command *c) { USE(v); USE(c); if (whyles == NULL) stderror(ERR_NAME | ERR_NOTWHILE); if (!noexec) toend(); } /*ARGSUSED*/ void doexit(Char **v, struct command *c) { USE(c); if (chkstop == 0 && (intty || intact) && evalvec == 0) panystop(0); /* * Don't DEMAND parentheses here either. */ v++; if (*v) { setv(STRstatus, putn(expr(&v)), VAR_READWRITE); if (*v) stderror(ERR_NAME | ERR_EXPRESSION); } btoeof(); #if 0 if (intty) #endif /* Always close, why only on ttys? */ xclose(SHIN); } /*ARGSUSED*/ void doforeach(Char **v, struct command *c) { Char *cp, *sp; struct whyle *nwp; int gflag; USE(c); v++; cp = sp = strip(*v); if (!letter(*cp)) stderror(ERR_NAME | ERR_VARBEGIN); do { cp++; } while (alnum(*cp)); if (*cp != '\0') stderror(ERR_NAME | ERR_VARALNUM); cp = *v++; if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') stderror(ERR_NAME | ERR_NOPAREN); v++; gflag = tglob(v); if (gflag) { v = globall(v, gflag); if (v == 0 && !noexec) stderror(ERR_NAME | ERR_NOMATCH); } else { v = saveblk(v); trim(v); } nwp = xcalloc(1, sizeof *nwp); nwp->w_fe = nwp->w_fe0 = v; btell(&nwp->w_start); nwp->w_fename = Strsave(cp); nwp->w_next = whyles; nwp->w_end.type = TCSH_F_SEEK; whyles = nwp; /* * Pre-read the loop so as to be more comprehensible to a terminal user. */ zlast = TC_FOREACH; if (intty) preread(); if (!noexec) doagain(); } /*ARGSUSED*/ void dowhile(Char **v, struct command *c) { int status; int again = whyles != 0 && SEEKEQ(&whyles->w_start, &lineloc) && whyles->w_fename == 0; USE(c); v++; /* * Implement prereading here also, taking care not to evaluate the * expression before the loop has been read up from a terminal. */ if (noexec) status = 0; else if (intty && !again) status = !exp0(&v, 1); else status = !expr(&v); if (*v && !noexec) stderror(ERR_NAME | ERR_EXPRESSION); if (!again) { struct whyle *nwp = xcalloc(1, sizeof(*nwp)); nwp->w_start = lineloc; nwp->w_end.type = TCSH_F_SEEK; nwp->w_end.f_seek = 0; nwp->w_end.a_seek = 0; nwp->w_next = whyles; whyles = nwp; zlast = TC_WHILE; if (intty) { /* * The tty preread */ preread(); doagain(); return; } } if (status) /* We ain't gonna loop no more, no more! */ toend(); } static void preread(void) { int old_pintr_disabled; whyles->w_end.type = TCSH_I_SEEK; if (setintr) pintr_push_enable(&old_pintr_disabled); search(TC_BREAK, 0, NULL); /* read the expression in */ if (setintr) cleanup_until(&old_pintr_disabled); btell(&whyles->w_end); } /*ARGSUSED*/ void doend(Char **v, struct command *c) { USE(v); USE(c); if (!whyles) stderror(ERR_NAME | ERR_NOTWHILE); btell(&whyles->w_end); if (!noexec) doagain(); } /*ARGSUSED*/ void docontin(Char **v, struct command *c) { USE(v); USE(c); if (!whyles) stderror(ERR_NAME | ERR_NOTWHILE); if (!noexec) doagain(); } static void doagain(void) { /* 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(NULL, NULL); return; } setv(whyles->w_fename, quote(Strsave(*whyles->w_fe++)), VAR_READWRITE); bseek(&whyles->w_start); } void dorepeat(Char **v, struct command *kp) { int i = 1; do { i *= getn(v[1]); lshift(v, 2); } while (v[0] != NULL && Strcmp(v[0], STRrepeat) == 0); if (noexec) i = 1; if (setintr) { pintr_disabled++; cleanup_push(&pintr_disabled, disabled_cleanup); } while (i > 0) { if (setintr && pintr_disabled == 1) { cleanup_until(&pintr_disabled); pintr_disabled++; cleanup_push(&pintr_disabled, disabled_cleanup); } reexecute(kp); --i; } if (setintr && pintr_disabled == 1) cleanup_until(&pintr_disabled); donefds(); } /*ARGSUSED*/ void doswbrk(Char **v, struct command *c) { USE(v); USE(c); if (!noexec) search(TC_BRKSW, 0, NULL); } int srchx(Char *cp) { struct srch *sp, *sp1, *sp2; int i; /* * Ignore keywords inside heredocs */ if (inheredoc) return -1; /* * 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, str2short(sp->s_name))) == 0) return sp->s_value; if (i < 0) sp2 = sp; else sp1 = sp + 1; } return (-1); } static const char * isrchx(int n) { struct srch *sp, *sp2; for (sp = srchn, sp2 = srchn + nsrchn; sp < sp2; sp++) if (sp->s_value == n) return (sp->s_name); return (""); } static int Stype; static Char *Sgoal; static void search(int type, int level, Char *goal) { struct Strbuf word = Strbuf_INIT; Char *cp; struct whyle *wp; int wlevel = 0; struct wordent *histent = NULL, *ohistent = NULL; Stype = type; Sgoal = goal; if (type == TC_GOTO) { struct Ain a; a.type = TCSH_F_SEEK; a.f_seek = 0; a.a_seek = 0; bseek(&a); } cleanup_push(&word, Strbuf_cleanup); do { if (intty) { histent = xmalloc(sizeof(*histent)); ohistent = xmalloc(sizeof(*histent)); ohistent->word = STRNULL; ohistent->next = histent; histent->prev = ohistent; } if (intty && fseekp == feobp && aret == TCSH_F_SEEK) printprompt(1, isrchx(type == TC_BREAK ? zlast : type)); /* xprintf("? "), flush(); */ (void) getword(&word); Strbuf_terminate(&word); if (intty && Strlen(word.s) > 0) { histent->word = Strsave(word.s); histent->next = xmalloc(sizeof(*histent)); histent->next->prev = histent; histent = histent->next; } switch (srchx(word.s)) { case TC_ELSE: if (level == 0 && type == TC_IF) goto end; break; case TC_IF: while (getword(&word)) { if (intty) { histent->word = Strsave(word.s); histent->next = xmalloc(sizeof(*histent)); histent->next->prev = histent; histent = histent->next; } continue; } if ((type == TC_IF || type == TC_ELSE) && eq(word.s, STRthen)) level++; break; case TC_ENDIF: if (type == TC_IF || type == TC_ELSE) level--; break; case TC_FOREACH: case TC_WHILE: wlevel++; if (type == TC_BREAK) level++; break; case TC_END: if (type == TC_BRKSW) { if (wlevel == 0) { wp = whyles; if (wp) { whyles = wp->w_next; wpfree(wp); } } } if (type == TC_BREAK) level--; wlevel--; break; case TC_SWITCH: if (type == TC_SWITCH || type == TC_BRKSW) level++; break; case TC_ENDSW: if (type == TC_SWITCH || type == TC_BRKSW) level--; break; case TC_LABEL: if (type == TC_GOTO && getword(&word) && eq(word.s, goal)) level = -1; break; default: if (type != TC_GOTO && (type != TC_SWITCH || level != 0)) break; if (word.len == 0 || word.s[word.len - 1] != ':') break; word.s[--word.len] = 0; if ((type == TC_GOTO && eq(word.s, goal)) || (type == TC_SWITCH && eq(word.s, STRdefault))) level = -1; break; case TC_CASE: if (type != TC_SWITCH || level != 0) break; (void) getword(&word); if (word.len != 0 && word.s[word.len - 1] == ':') word.s[--word.len] = 0; cp = strip(Dfix1(word.s)); cleanup_push(cp, xfree); if (Gmatch(goal, cp)) level = -1; cleanup_until(cp); break; case TC_DEFAULT: if (type == TC_SWITCH && level == 0) level = -1; break; } if (intty) { ohistent->prev = histgetword(histent); ohistent->prev->next = ohistent; savehist(ohistent, 0); freelex(ohistent); xfree(ohistent); } else (void) getword(NULL); } while (level >= 0); end: cleanup_until(&word); } static struct wordent * histgetword(struct wordent *histent) { int first; eChar c, d; int e; struct Strbuf *tmp; tmp = xmalloc(sizeof(*tmp)); tmp->size = 0; tmp->s = NULL; c = readc(1); d = 0; e = 0; for (;;) { tmp->len = 0; Strbuf_terminate (tmp); while (c == ' ' || c == '\t') c = readc(1); if (c == '#') do c = readc(1); while (c != CHAR_ERR && c != '\n'); if (c == CHAR_ERR) goto past; if (c == '\n') goto nl; unreadc(c); first = 1; do { e = (c == '\\'); c = readc(1); if (c == '\\' && !e) { if ((c = readc(1)) == '\n') { e = 1; c = ' '; } else { unreadc(c); c = '\\'; } } if ((c == '\'' || c == '"') && !e) { if (d == 0) d = c; else if (d == c) d = 0; } if (c == CHAR_ERR) goto past; Strbuf_append1(tmp, (Char) c); if (!first && !d && c == '(' && !e) { break; } first = 0; } while (d || e || (c != ' ' && c != '\t' && c != '\n')); tmp->len--; if (tmp->len) { Strbuf_terminate(tmp); histent->word = Strsave(tmp->s); histent->next = xmalloc(sizeof (*histent)); histent->next->prev = histent; histent = histent->next; } if (c == '\n') { nl: tmp->len = 0; Strbuf_append1(tmp, (Char) c); Strbuf_terminate(tmp); histent->word = Strsave(tmp->s); return histent; } } past: switch (Stype) { case TC_IF: stderror(ERR_NAME | ERR_NOTFOUND, "then/endif"); break; case TC_ELSE: stderror(ERR_NAME | ERR_NOTFOUND, "endif"); break; case TC_BRKSW: case TC_SWITCH: stderror(ERR_NAME | ERR_NOTFOUND, "endsw"); break; case TC_BREAK: stderror(ERR_NAME | ERR_NOTFOUND, "end"); break; case TC_GOTO: setname(short2str(Sgoal)); stderror(ERR_NAME | ERR_NOTFOUND, "label"); break; default: break; } /* NOTREACHED */ return NULL; } static int getword(struct Strbuf *wp) { int found = 0, first; eChar c, d; if (wp) wp->len = 0; c = readc(1); d = 0; do { while (c == ' ' || c == '\t') c = readc(1); if (c == '#') do c = readc(1); while (c != CHAR_ERR && c != '\n'); if (c == CHAR_ERR) goto past; if (c == '\n') { if (wp) break; return (0); } unreadc(c); found = 1; first = 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 == CHAR_ERR) goto past; if (wp) Strbuf_append1(wp, (Char) c); if (!d && c == ')') { if (!first && wp) { goto past_word_end; } else { if (wp) { wp->len = 1; Strbuf_terminate(wp); } return found; } } if (!first && !d && c == '(') { if (wp) goto past_word_end; else break; } first = 0; } while ((d || (c != ' ' && c != '\t')) && c != '\n'); } while (wp == 0); past_word_end: unreadc(c); if (found) { wp->len--; Strbuf_terminate(wp); } return (found); past: switch (Stype) { case TC_IF: stderror(ERR_NAME | ERR_NOTFOUND, "then/endif"); break; case TC_ELSE: stderror(ERR_NAME | ERR_NOTFOUND, "endif"); break; case TC_BRKSW: case TC_SWITCH: stderror(ERR_NAME | ERR_NOTFOUND, "endsw"); break; case TC_BREAK: stderror(ERR_NAME | ERR_NOTFOUND, "end"); break; case TC_GOTO: setname(short2str(Sgoal)); stderror(ERR_NAME | ERR_NOTFOUND, "label"); break; default: break; } /* NOTREACHED */ return (0); } static void toend(void) { if (whyles->w_end.type == TCSH_F_SEEK && whyles->w_end.f_seek == 0) { search(TC_BREAK, 0, NULL); btell(&whyles->w_end); whyles->w_end.f_seek--; } else { bseek(&whyles->w_end); } wfree(); } static void wpfree(struct whyle *wp) { if (wp->w_fe0) blkfree(wp->w_fe0); xfree(wp->w_fename); xfree(wp); } void wfree(void) { struct Ain o; struct whyle *nwp; #ifdef lint nwp = NULL; /* sun lint is dumb! */ #endif #ifdef FDEBUG static const char foo[] = "IAFE"; #endif /* FDEBUG */ btell(&o); #ifdef FDEBUG xprintf("o->type %c o->a_seek %d o->f_seek %d\n", foo[o.type + 1], o.a_seek, o.f_seek); #endif /* FDEBUG */ for (; whyles; whyles = nwp) { struct whyle *wp = whyles; nwp = wp->w_next; #ifdef FDEBUG xprintf("start->type %c start->a_seek %d start->f_seek %d\n", foo[wp->w_start.type+1], wp->w_start.a_seek, wp->w_start.f_seek); xprintf("end->type %c end->a_seek %d end->f_seek %d\n", foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek); #endif /* FDEBUG */ /* * XXX: We free loops that have different seek types. */ if (wp->w_end.type != TCSH_I_SEEK && wp->w_start.type == wp->w_end.type && wp->w_start.type == o.type) { if (wp->w_end.type == TCSH_F_SEEK) { if (o.f_seek >= wp->w_start.f_seek && (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek)) break; } else { if (o.a_seek >= wp->w_start.a_seek && (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek)) break; } } wpfree(wp); } } /*ARGSUSED*/ void doecho(Char **v, struct command *c) { USE(c); xecho(' ', v); } /*ARGSUSED*/ void doglob(Char **v, struct command *c) { USE(c); xecho(0, v); flush(); } static void xecho(int sep, Char **v) { Char *cp, **globbed = NULL; int nonl = 0; int echo_style = ECHO_STYLE; struct varent *vp; if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL && vp->vec[0] != NULL) { if (Strcmp(vp->vec[0], STRbsd) == 0) echo_style = BSD_ECHO; else if (Strcmp(vp->vec[0], STRsysv) == 0) echo_style = SYSV_ECHO; else if (Strcmp(vp->vec[0], STRboth) == 0) echo_style = BOTH_ECHO; else if (Strcmp(vp->vec[0], STRnone) == 0) echo_style = NONE_ECHO; } v++; if (*v == 0) goto done; if (setintr) { int old_pintr_disabled; pintr_push_enable(&old_pintr_disabled); v = glob_all_or_error(v); cleanup_until(&old_pintr_disabled); } else { v = glob_all_or_error(v); } globbed = v; if (globbed != NULL) cleanup_push(globbed, blk_cleanup); if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn)) nonl++, v++; while ((cp = *v++) != 0) { Char c; if (setintr) { int old_pintr_disabled; pintr_push_enable(&old_pintr_disabled); cleanup_until(&old_pintr_disabled); } while ((c = *cp++) != 0) { if ((echo_style & SYSV_ECHO) != 0 && c == '\\') { switch (c = *cp++) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'c': nonl = 1; goto done; case 'e': #if 0 /* Windows does not understand \e */ c = '\e'; #else c = CTL_ESC('\033'); #endif break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case '\\': c = '\\'; break; case '0': c = 0; if (*cp >= '0' && *cp < '8') c = c * 8 + *cp++ - '0'; if (*cp >= '0' && *cp < '8') c = c * 8 + *cp++ - '0'; if (*cp >= '0' && *cp < '8') c = c * 8 + *cp++ - '0'; break; case '\0': c = '\\'; cp--; break; default: xputchar('\\' | QUOTE); break; } } xputwchar(c | QUOTE); } if (*v) xputchar(sep | QUOTE); } done: if (sep && nonl == 0) xputchar('\n'); else flush(); if (globbed != NULL) cleanup_until(globbed); } /* check whether an environment variable should invoke 'set_locale()' */ static int islocale_var(Char *var) { static Char *locale_vars[] = { STRLANG, STRLC_ALL, STRLC_CTYPE, STRLC_NUMERIC, STRLC_TIME, STRLC_COLLATE, STRLC_MESSAGES, STRLC_MONETARY, 0 }; Char **v; for (v = locale_vars; *v; ++v) if (eq(var, *v)) return 1; return 0; } static void xlate_cr_cleanup(void *dummy) { USE(dummy); xlate_cr = 0; } /*ARGSUSED*/ void doprintenv(Char **v, struct command *c) { Char *e; USE(c); v++; if (*v == 0) { Char **ep; xlate_cr = 1; cleanup_push(&xlate_cr, xlate_cr_cleanup); for (ep = STR_environ; *ep; ep++) { if (setintr) { int old_pintr_disabled; pintr_push_enable(&old_pintr_disabled); cleanup_until(&old_pintr_disabled); } xprintf("%S\n", *ep); } cleanup_until(&xlate_cr); } else if ((e = tgetenv(*v)) != NULL) { int old_output_raw; old_output_raw = output_raw; output_raw = 1; cleanup_push(&old_output_raw, output_raw_restore); xprintf("%S\n", e); cleanup_until(&old_output_raw); } else setcopy(STRstatus, STR1, VAR_READWRITE); } /* from "Karl Berry." -- for NeXT things (and anything else with a modern compiler) */ /*ARGSUSED*/ void dosetenv(Char **v, struct command *c) { Char *vp, *lp; USE(c); if (*++v == 0) { doprintenv(--v, 0); return; } vp = *v++; lp = vp; if (!letter(*lp)) stderror(ERR_NAME | ERR_VARBEGIN); do { lp++; } while (alnum(*lp) || *lp == '.'); if (*lp != '\0') stderror(ERR_NAME | ERR_VARALNUM); if ((lp = *v++) == 0) lp = STRNULL; lp = globone(lp, G_APPEND); cleanup_push(lp, xfree); tsetenv(vp, lp); if (eq(vp, STRKPATH)) { importpath(lp); dohash(NULL, NULL); cleanup_until(lp); return; } #ifdef apollo if (eq(vp, STRSYSTYPE)) { dohash(NULL, NULL); cleanup_until(lp); return; } #endif /* apollo */ /* dspkanji/dspmbyte autosetting */ /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */ #if defined(DSPMBYTE) if(eq(vp, STRLANG) && !adrof(CHECK_MBYTEVAR)) { autoset_dspmbyte(lp); } #endif if (islocale_var(vp)) { #ifdef NLS int k; # ifdef SETLOCALEBUG dont_free = 1; # endif /* SETLOCALEBUG */ (void) setlocale(LC_ALL, ""); # ifdef LC_COLLATE (void) setlocale(LC_COLLATE, ""); # endif # ifdef LC_CTYPE (void) setlocale(LC_CTYPE, ""); /* for iscntrl */ # endif /* LC_CTYPE */ # if defined(AUTOSET_KANJI) autoset_kanji(); # endif /* AUTOSET_KANJI */ # ifdef NLS_CATALOGS # ifdef LC_MESSAGES (void) setlocale(LC_MESSAGES, ""); # endif /* LC_MESSAGES */ nlsclose(); nlsinit(); # endif /* NLS_CATALOGS */ # ifdef SETLOCALEBUG dont_free = 0; # endif /* SETLOCALEBUG */ # ifdef STRCOLLBUG fix_strcoll_bug(); # endif /* STRCOLLBUG */ tw_cmd_free(); /* since the collation sequence has changed */ for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++) continue; AsciiOnly = MB_CUR_MAX == 1 && k > 0377; #else /* !NLS */ AsciiOnly = 0; #endif /* NLS */ NLSMapsAreInited = 0; ed_Init(); if (MapsAreInited && !NLSMapsAreInited) ed_InitNLSMaps(); cleanup_until(lp); return; } #ifdef NLS_CATALOGS if (eq(vp, STRNLSPATH)) { nlsclose(); nlsinit(); } #endif if (eq(vp, STRNOREBIND)) { NoNLSRebind = 1; MapsAreInited = 0; NLSMapsAreInited = 0; ed_InitMaps(); cleanup_until(lp); return; } #ifdef WINNT_NATIVE if (eq(vp, STRtcshlang)) { nlsinit(); cleanup_until(lp); return; } #endif /* WINNT_NATIVE */ if (eq(vp, STRKTERM)) { char *t; setv(STRterm, quote(lp), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(lp); cleanup_until(lp); t = short2str(lp); if (noediting && strcmp(t, "unknown") != 0 && strcmp(t,"dumb") != 0) { editing = 1; noediting = 0; setNS(STRedit); } GotTermCaps = 0; ed_Init(); return; } if (eq(vp, STRKHOME)) { Char *canon; /* * convert to canonical pathname (possibly resolving symlinks) */ canon = dcanon(lp, lp); cleanup_ignore(lp); cleanup_until(lp); cleanup_push(canon, xfree); setv(STRhome, quote(canon), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(canon); cleanup_until(canon); /* fix directory stack for new tilde home */ dtilde(); return; } if (eq(vp, STRKSHLVL)) { setv(STRshlvl, quote(lp), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(lp); cleanup_until(lp); return; } if (eq(vp, STRKUSER)) { setv(STRuser, quote(lp), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(lp); cleanup_until(lp); return; } if (eq(vp, STRKGROUP)) { setv(STRgroup, quote(lp), VAR_READWRITE); /* lp memory used here */ cleanup_ignore(lp); cleanup_until(lp); return; } #ifdef COLOR_LS_F if (eq(vp, STRLS_COLORS)) { parseLS_COLORS(lp); cleanup_until(lp); return; } if (eq(vp, STRLSCOLORS)) { parseLSCOLORS(lp); cleanup_until(lp); return; } #endif /* COLOR_LS_F */ #ifdef SIG_WINDOW /* * Load/Update $LINES $COLUMNS */ if ((eq(lp, STRNULL) && (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) || eq(vp, STRTERMCAP)) { cleanup_until(lp); check_window_size(1); return; } /* * Change the size to the one directed by $LINES and $COLUMNS */ if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) { #if 0 GotTermCaps = 0; #endif cleanup_until(lp); ed_Init(); return; } #endif /* SIG_WINDOW */ cleanup_until(lp); } /*ARGSUSED*/ void dounsetenv(Char **v, struct command *c) { Char **ep, *p, *n, *name; int i, maxi; USE(c); /* * Find the longest environment variable */ for (maxi = 0, ep = STR_environ; *ep; ep++) { for (i = 0, p = *ep; *p && *p != '='; p++, i++) continue; if (i > maxi) maxi = i; } name = xmalloc((maxi + 1) * sizeof(Char)); cleanup_push(name, xfree); while (++v && *v) for (maxi = 1; maxi;) for (maxi = 0, ep = STR_environ; *ep; ep++) { for (n = name, p = *ep; *p && *p != '='; *n++ = *p++) continue; *n = '\0'; if (!Gmatch(name, *v)) continue; maxi = 1; /* Unset the name. This wasn't being done until * later but most of the stuff following won't * work (particularly the setlocale() and getenv() * stuff) as intended until the name is actually * removed. (sg) */ Unsetenv(name); if (eq(name, STRNOREBIND)) { NoNLSRebind = 0; MapsAreInited = 0; NLSMapsAreInited = 0; ed_InitMaps(); } #ifdef apollo else if (eq(name, STRSYSTYPE)) dohash(NULL, NULL); #endif /* apollo */ else if (islocale_var(name)) { #ifdef NLS int k; # ifdef SETLOCALEBUG dont_free = 1; # endif /* SETLOCALEBUG */ (void) setlocale(LC_ALL, ""); # ifdef LC_COLLATE (void) setlocale(LC_COLLATE, ""); # endif # ifdef LC_CTYPE (void) setlocale(LC_CTYPE, ""); /* for iscntrl */ # endif /* LC_CTYPE */ # ifdef NLS_CATALOGS # ifdef LC_MESSAGES (void) setlocale(LC_MESSAGES, ""); # endif /* LC_MESSAGES */ nlsclose(); nlsinit(); # endif /* NLS_CATALOGS */ # ifdef SETLOCALEBUG dont_free = 0; # endif /* SETLOCALEBUG */ # ifdef STRCOLLBUG fix_strcoll_bug(); # endif /* STRCOLLBUG */ tw_cmd_free();/* since the collation sequence has changed */ for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++) continue; AsciiOnly = MB_CUR_MAX == 1 && k > 0377; #else /* !NLS */ AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; #endif /* NLS */ NLSMapsAreInited = 0; ed_Init(); if (MapsAreInited && !NLSMapsAreInited) ed_InitNLSMaps(); } #ifdef WINNT_NATIVE else if (eq(name,(STRtcshlang))) { nls_dll_unload(); nlsinit(); } #endif /* WINNT_NATIVE */ #ifdef COLOR_LS_F else if (eq(name, STRLS_COLORS)) parseLS_COLORS(n); else if (eq(name, STRLSCOLORS)) parseLSCOLORS(n); #endif /* COLOR_LS_F */ #ifdef NLS_CATALOGS else if (eq(name, STRNLSPATH)) { nlsclose(); nlsinit(); } #endif /* * start again cause the environment changes */ break; } cleanup_until(name); } void tsetenv(const Char *name, const Char *val) { #ifdef SETENV_IN_LIB /* * XXX: This does not work right, since tcsh cannot track changes to * the environment this way. (the builtin setenv without arguments does * not print the right stuff neither does unsetenv). This was for Mach, * it is not needed anymore. */ #undef setenv char *cname; if (name == NULL) return; cname = strsave(short2str(name)); setenv(cname, short2str(val), 1); xfree(cname); #else /* !SETENV_IN_LIB */ Char **ep = STR_environ; const Char *ccp; Char *cp, *dp; Char *blk[2]; Char **oep = ep; #ifdef WINNT_NATIVE nt_set_env(name,val); #endif /* WINNT_NATIVE */ for (; *ep; ep++) { #ifdef WINNT_NATIVE for (ccp = name, dp = *ep; *ccp && Tolower(*ccp & TRIM) == Tolower(*dp); ccp++, dp++) #else for (ccp = name, dp = *ep; *ccp && (*ccp & TRIM) == *dp; ccp++, dp++) #endif /* WINNT_NATIVE */ continue; if (*ccp != 0 || *dp != '=') continue; cp = Strspl(STRequal, val); xfree(*ep); *ep = strip(Strspl(name, cp)); xfree(cp); blkfree((Char **) environ); environ = short2blk(STR_environ); return; } cp = Strspl(name, STRequal); blk[0] = strip(Strspl(cp, val)); xfree(cp); blk[1] = 0; STR_environ = blkspl(STR_environ, blk); blkfree((Char **) environ); environ = short2blk(STR_environ); xfree(oep); #endif /* SETENV_IN_LIB */ } void Unsetenv(Char *name) { Char **ep = STR_environ; Char *cp, *dp; Char **oep = ep; #ifdef WINNT_NATIVE nt_set_env(name,NULL); #endif /*WINNT_NATIVE */ for (; *ep; ep++) { for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++) continue; if (*cp != 0 || *dp != '=') continue; cp = *ep; *ep = 0; STR_environ = blkspl(STR_environ, ep + 1); blkfree((Char **) environ); environ = short2blk(STR_environ); *ep = cp; xfree(cp); xfree(oep); return; } } /*ARGSUSED*/ void doumask(Char **v, struct command *c) { Char *cp = v[1]; int i; USE(c); if (cp == 0) { i = (int)umask(0); (void) umask(i); xprintf("%o\n", i); return; } i = 0; while (Isdigit(*cp) && *cp != '8' && *cp != '9') i = i * 8 + *cp++ - '0'; if (*cp || i < 0 || i > 0777) stderror(ERR_NAME | ERR_MASK); (void) umask(i); } #ifndef HAVENOLIMIT # ifndef BSDLIMIT typedef long RLIM_TYPE; # ifdef _OSD_POSIX /* BS2000 */ # include # endif # ifndef RLIM_INFINITY # if !defined(_MINIX) && !defined(__clipper__) && !defined(_CRAY) extern RLIM_TYPE ulimit(); # endif /* ! _MINIX && !__clipper__ */ # define RLIM_INFINITY 0x003fffff # define RLIMIT_FSIZE 1 # endif /* RLIM_INFINITY */ # ifdef aiws # define toset(a) (((a) == 3) ? 1004 : (a) + 1) # define RLIMIT_DATA 3 # define RLIMIT_STACK 1005 # else /* aiws */ # define toset(a) ((a) + 1) # endif /* aiws */ # else /* BSDLIMIT */ # if (defined(BSD4_4) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || (HPUXVERSION >= 1100)) && !defined(__386BSD__) typedef rlim_t RLIM_TYPE; # else # if defined(SOLARIS2) || (defined(sgi) && SYSVREL > 3) typedef rlim_t RLIM_TYPE; # else # if defined(_SX) typedef long long RLIM_TYPE; # else /* !_SX */ typedef unsigned long RLIM_TYPE; # endif /* _SX */ # endif /* SOLARIS2 || (sgi && SYSVREL > 3) */ # endif /* BSD4_4 && !__386BSD__ */ # endif /* BSDLIMIT */ # if (HPUXVERSION > 700) && (HPUXVERSION < 1100) && defined(BSDLIMIT) /* Yes hpux8.0 has limits but does not make them public */ /* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */ # ifndef RLIMIT_CPU # define RLIMIT_CPU 0 # define RLIMIT_FSIZE 1 # define RLIMIT_DATA 2 # define RLIMIT_STACK 3 # define RLIMIT_CORE 4 # define RLIMIT_RSS 5 # define RLIMIT_NOFILE 6 # endif /* RLIMIT_CPU */ # ifndef RLIM_INFINITY # define RLIM_INFINITY 0x7fffffff # endif /* RLIM_INFINITY */ /* * old versions of HP/UX counted limits in 512 bytes */ # ifndef SIGRTMIN # define FILESIZE512 # endif /* SIGRTMIN */ # endif /* (HPUXVERSION > 700) && (HPUXVERSION < 1100) && BSDLIMIT */ # if SYSVREL > 3 && defined(BSDLIMIT) && !defined(_SX) /* In order to use rusage, we included "/usr/ucbinclude/sys/resource.h" in */ /* sh.h. However, some SVR4 limits are defined in . Rather */ /* than include both and get warnings, we define the extra SVR4 limits here. */ /* XXX: I don't understand if RLIMIT_AS is defined, why don't we define */ /* RLIMIT_VMEM based on it? */ # ifndef RLIMIT_VMEM # define RLIMIT_VMEM 6 # endif # ifndef RLIMIT_AS # define RLIMIT_AS RLIMIT_VMEM # endif # endif /* SYSVREL > 3 && BSDLIMIT */ # if (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) # if defined(RLIMIT_AS) && !defined(RLIMIT_VMEM) # define RLIMIT_VMEM RLIMIT_AS # endif /* * Oh well, has it, but does not * Linux headers: When the left hand does not know what the right hand does. */ # if defined(RLIMIT_RTPRIO) && !defined(RLIMIT_RTTIME) # define RLIMIT_RTTIME (RLIMIT_RTPRIO + 1) # endif # endif struct limits limits[] = { # ifdef RLIMIT_CPU { RLIMIT_CPU, "cputime", 1, "seconds" }, # endif /* RLIMIT_CPU */ # ifdef RLIMIT_FSIZE # ifndef aiws { RLIMIT_FSIZE, "filesize", 1024, "kbytes" }, # else { RLIMIT_FSIZE, "filesize", 512, "blocks" }, # endif /* aiws */ # endif /* RLIMIT_FSIZE */ # ifdef RLIMIT_DATA { RLIMIT_DATA, "datasize", 1024, "kbytes" }, # endif /* RLIMIT_DATA */ # ifdef RLIMIT_STACK # ifndef aiws { RLIMIT_STACK, "stacksize", 1024, "kbytes" }, # else { RLIMIT_STACK, "stacksize", 1024 * 1024, "kbytes"}, # endif /* aiws */ # endif /* RLIMIT_STACK */ # ifdef RLIMIT_CORE { RLIMIT_CORE, "coredumpsize", 1024, "kbytes" }, # endif /* RLIMIT_CORE */ # ifdef RLIMIT_RSS { RLIMIT_RSS, "memoryuse", 1024, "kbytes" }, # endif /* RLIMIT_RSS */ # ifdef RLIMIT_UMEM { RLIMIT_UMEM, "memoryuse", 1024, "kbytes" }, # endif /* RLIMIT_UMEM */ # ifdef RLIMIT_VMEM { RLIMIT_VMEM, "vmemoryuse", 1024, "kbytes" }, # endif /* RLIMIT_VMEM */ # if defined(RLIMIT_HEAP) /* found on BS2000/OSD systems */ { RLIMIT_HEAP, "heapsize", 1024, "kbytes" }, # endif /* RLIMIT_HEAP */ # ifdef RLIMIT_NOFILE { RLIMIT_NOFILE, "descriptors", 1, "" }, # endif /* RLIMIT_NOFILE */ # ifdef RLIMIT_NPTS { RLIMIT_NPTS, "pseudoterminals", 1, "" }, # endif /* RLIMIT_NPTS */ # ifdef RLIMIT_KQUEUES { RLIMIT_KQUEUES, "kqueues", 1, "" }, # endif /* RLIMIT_KQUEUES */ # ifdef RLIMIT_CONCUR { RLIMIT_CONCUR, "concurrency", 1, "thread(s)" }, # endif /* RLIMIT_CONCUR */ # ifdef RLIMIT_MEMLOCK { RLIMIT_MEMLOCK, "memorylocked", 1024, "kbytes" }, # endif /* RLIMIT_MEMLOCK */ # ifdef RLIMIT_NPROC { RLIMIT_NPROC, "maxproc", 1, "" }, # endif /* RLIMIT_NPROC */ # ifdef RLIMIT_NTHR { RLIMIT_NTHR, "maxthread", 1, "" }, # endif /* RLIMIT_NTHR */ # if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE) { RLIMIT_OFILE, "openfiles", 1, "" }, # endif /* RLIMIT_OFILE && !defined(RLIMIT_NOFILE) */ # ifdef RLIMIT_SBSIZE { RLIMIT_SBSIZE, "sbsize", 1, "" }, # endif /* RLIMIT_SBSIZE */ # ifdef RLIMIT_SWAP { RLIMIT_SWAP, "swapsize", 1024, "kbytes" }, # endif /* RLIMIT_SWAP */ # ifdef RLIMIT_LOCKS { RLIMIT_LOCKS, "maxlocks", 1, "" }, # endif /* RLIMIT_LOCKS */ # ifdef RLIMIT_POSIXLOCKS { RLIMIT_POSIXLOCKS,"posixlocks", 1, "" }, # endif /* RLIMIT_POSIXLOCKS */ # ifdef RLIMIT_SIGPENDING { RLIMIT_SIGPENDING,"maxsignal", 1, "" }, # endif /* RLIMIT_SIGPENDING */ # ifdef RLIMIT_MSGQUEUE { RLIMIT_MSGQUEUE, "maxmessage", 1, "" }, # endif /* RLIMIT_MSGQUEUE */ # ifdef RLIMIT_NICE { RLIMIT_NICE, "maxnice", 1, "" }, # endif /* RLIMIT_NICE */ # ifdef RLIMIT_RTPRIO { RLIMIT_RTPRIO, "maxrtprio", 1, "" }, # endif /* RLIMIT_RTPRIO */ # ifdef RLIMIT_RTTIME { RLIMIT_RTTIME, "maxrttime", 1, "usec" }, # endif /* RLIMIT_RTTIME */ { -1, NULL, 0, NULL } }; static struct limits *findlim (Char *); static RLIM_TYPE getval (struct limits *, Char **); static int strtail (Char *, const char *); static void limtail (Char *, const char *); static void limtail2 (Char *, const char *, const char *); static void plim (struct limits *, int); static int setlim (struct limits *, int, RLIM_TYPE); #ifdef convex static RLIM_TYPE restrict_limit(double value) { /* * is f too large to cope with? return the maximum or minimum int */ if (value > (double) INT_MAX) return (RLIM_TYPE) INT_MAX; else if (value < (double) INT_MIN) return (RLIM_TYPE) INT_MIN; else return (RLIM_TYPE) value; } #else /* !convex */ # define restrict_limit(x) ((RLIM_TYPE) (x)) #endif /* convex */ static struct limits * findlim(Char *cp) { struct limits *lp, *res; res = NULL; for (lp = limits; lp->limconst >= 0; lp++) if (prefix(cp, str2short(lp->limname))) { if (res) stderror(ERR_NAME | ERR_AMBIG); res = lp; } if (res) return (res); stderror(ERR_NAME | ERR_LIMIT); /* NOTREACHED */ return (0); } /*ARGSUSED*/ void dolimit(Char **v, struct command *c) { struct limits *lp; RLIM_TYPE limit; int hard = 0; USE(c); v++; if (*v && eq(*v, STRmh)) { 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; } limit = getval(lp, v + 1); if (setlim(lp, hard, limit) < 0) stderror(ERR_SILENT); } static RLIM_TYPE getval(struct limits *lp, Char **v) { float f; Char *cp = *v++; f = atof(short2str(cp)); # ifdef convex /* * is f too large to cope with. limit f to minint, maxint - X-6768 by * strike */ if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) { stderror(ERR_NAME | ERR_TOOLARGE); } # endif /* convex */ while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E') cp++; if (*cp == 0) { if (*v == 0) return restrict_limit((f * lp->limdiv) + 0.5); cp = *v; } switch (*cp) { # ifdef RLIMIT_CPU case ':': if (lp->limconst != RLIMIT_CPU) goto badscal; return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f * 60.0 + atof(short2str(cp + 1)))); case 'h': if (lp->limconst != RLIMIT_CPU) goto badscal; limtail(cp, "hours"); f *= 3600.0; break; # endif /* RLIMIT_CPU */ case 'm': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) { limtail(cp, "minutes"); f *= 60.0; break; } # endif /* RLIMIT_CPU */ limtail2(cp, "megabytes", "mbytes"); f *= 1024.0 * 1024.0; break; # ifdef RLIMIT_CPU case 's': if (lp->limconst != RLIMIT_CPU) goto badscal; limtail(cp, "seconds"); break; # endif /* RLIMIT_CPU */ case 'G': *cp = 'g'; /*FALLTHROUGH*/ case 'g': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) goto badscal; # endif /* RLIMIT_CPU */ limtail2(cp, "gigabytes", "gbytes"); f *= 1024.0 * 1024.0 * 1024.0; break; case 'M': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) goto badscal; # endif /* RLIMIT_CPU */ *cp = 'm'; limtail2(cp, "megabytes", "mbytes"); f *= 1024.0 * 1024.0; break; case 'k': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) goto badscal; # endif /* RLIMIT_CPU */ limtail2(cp, "kilobytes", "kbytes"); f *= 1024.0; break; case 'b': # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) goto badscal; # endif /* RLIMIT_CPU */ limtail(cp, "blocks"); f *= 512.0; break; case 'u': limtail(cp, "unlimited"); return ((RLIM_TYPE) RLIM_INFINITY); default: # ifdef RLIMIT_CPU badscal: # endif /* RLIMIT_CPU */ stderror(ERR_NAME | ERR_SCALEF); } # ifdef convex return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f + 0.5)); # else f += 0.5; if (f > (float) ((RLIM_TYPE) RLIM_INFINITY)) return ((RLIM_TYPE) RLIM_INFINITY); else return ((RLIM_TYPE) f); # endif /* convex */ } static int strtail(Char *cp, const char *str) { while (*cp && *cp == (Char)*str) cp++, str++; return (*cp != '\0'); } static void limtail(Char *cp, const char *str) { if (strtail(cp, str)) stderror(ERR_BADSCALE, str); } static void limtail2(Char *cp, const char *str1, const char *str2) { if (strtail(cp, str1) && strtail(cp, str2)) stderror(ERR_BADSCALE, str1); } /*ARGSUSED*/ static void plim(struct limits *lp, int hard) { # ifdef BSDLIMIT struct rlimit rlim; # endif /* BSDLIMIT */ RLIM_TYPE limit; int xdiv = lp->limdiv; xprintf("%-13.13s", lp->limname); # ifndef BSDLIMIT limit = ulimit(lp->limconst, 0); # ifdef aiws if (lp->limconst == RLIMIT_DATA) limit -= 0x20000000; # endif /* aiws */ # else /* BSDLIMIT */ (void) getrlimit(lp->limconst, &rlim); limit = hard ? rlim.rlim_max : rlim.rlim_cur; # endif /* BSDLIMIT */ # if !defined(BSDLIMIT) || defined(FILESIZE512) /* * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024 * blocks. Note we cannot pre-multiply cause we might overflow (A/UX) */ if (lp->limconst == RLIMIT_FSIZE) { if (limit >= (RLIM_INFINITY / 512)) limit = RLIM_INFINITY; else xdiv = (xdiv == 1024 ? 2 : 1); } # endif /* !BSDLIMIT || FILESIZE512 */ if (limit == RLIM_INFINITY) xprintf("unlimited"); else # if defined(RLIMIT_CPU) && defined(_OSD_POSIX) if (lp->limconst == RLIMIT_CPU && (unsigned long)limit >= 0x7ffffffdUL) xprintf("unlimited"); else # endif # ifdef RLIMIT_CPU if (lp->limconst == RLIMIT_CPU) psecs(limit); else # endif /* RLIMIT_CPU */ xprintf("%ld %s", (long) (limit / xdiv), lp->limscale); xputchar('\n'); } /*ARGSUSED*/ void dounlimit(Char **v, struct command *c) { struct limits *lp; int lerr = 0; int hard = 0; int force = 0; USE(c); while (*++v && **v == '-') { Char *vp = *v; while (*++vp) switch (*vp) { case 'f': force = 1; break; case 'h': hard = 1; break; default: stderror(ERR_ULIMUS); break; } } if (*v == 0) { for (lp = limits; lp->limconst >= 0; lp++) if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0) lerr++; if (!force && lerr) stderror(ERR_SILENT); return; } while (*v) { lp = findlim(*v++); if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0 && !force) stderror(ERR_SILENT); } } static int setlim(struct limits *lp, int hard, RLIM_TYPE limit) { # ifdef BSDLIMIT struct rlimit rlim; (void) getrlimit(lp->limconst, &rlim); # ifdef FILESIZE512 /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */ if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE) limit /= 512; # endif /* FILESIZE512 */ if (hard) rlim.rlim_max = limit; else if (limit == RLIM_INFINITY && euid != 0) rlim.rlim_cur = rlim.rlim_max; else rlim.rlim_cur = limit; if (rlim.rlim_cur > rlim.rlim_max) rlim.rlim_max = rlim.rlim_cur; if (setrlimit(lp->limconst, &rlim) < 0) { # else /* BSDLIMIT */ if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE) limit /= 512; # ifdef aiws if (lp->limconst == RLIMIT_DATA) limit += 0x20000000; # endif /* aiws */ if (ulimit(toset(lp->limconst), limit) < 0) { # endif /* BSDLIMIT */ int err; char *op, *type; err = errno; op = strsave(limit == RLIM_INFINITY ? CGETS(15, 2, "remove") : CGETS(15, 3, "set")); cleanup_push(op, xfree); type = strsave(hard ? CGETS(15, 4, " hard") : ""); cleanup_push(type, xfree); xprintf(CGETS(15, 1, "%s: %s: Can't %s%s limit (%s)\n"), bname, lp->limname, op, type, strerror(err)); cleanup_until(op); return (-1); } return (0); } #endif /* !HAVENOLIMIT */ /*ARGSUSED*/ void dosuspend(Char **v, struct command *c) { #ifdef BSDJOBS struct sigaction old; #endif /* BSDJOBS */ USE(c); USE(v); if (loginsh) stderror(ERR_SUSPLOG); untty(); #ifdef BSDJOBS sigaction(SIGTSTP, NULL, &old); signal(SIGTSTP, SIG_DFL); (void) kill(0, SIGTSTP); /* the shell stops here */ sigaction(SIGTSTP, &old, NULL); #else /* !BSDJOBS */ stderror(ERR_JOBCONTROL); #endif /* BSDJOBS */ #ifdef BSDJOBS if (tpgrp != -1) { if (grabpgrp(FSHTTY, opgrp) == -1) stderror(ERR_SYSTEM, "tcgetpgrp", strerror(errno)); (void) setpgid(0, shpgrp); (void) tcsetpgrp(FSHTTY, shpgrp); } #endif /* BSDJOBS */ (void) setdisc(FSHTTY); } /* This is the dreaded EVAL built-in. * If you don't fiddle with file descriptors, and reset didfds, * this command will either ignore redirection inside or outside * its arguments, e.g. eval "date >x" vs. eval "date" >x * The stuff here seems to work, but I did it by trial and error rather * than really knowing what was going on. If tpgrp is zero, we are * probably a background eval, e.g. "eval date &", and we want to * make sure that any processes we start stay in our pgrp. * This is also the case for "time eval date" -- stay in same pgrp. * Otherwise, under stty tostop, processes will stop in the wrong * pgrp, with no way for the shell to get them going again. -IAN! */ struct doeval_state { Char **evalvec, *evalp; int didfds; #ifndef CLOSE_ON_EXEC int didcch; #endif int saveIN, saveOUT, saveDIAG; int SHIN, SHOUT, SHDIAG; }; static void doeval_cleanup(void *xstate) { struct doeval_state *state; state = xstate; evalvec = state->evalvec; evalp = state->evalp; doneinp = 0; #ifndef CLOSE_ON_EXEC didcch = state->didcch; #endif /* CLOSE_ON_EXEC */ didfds = state->didfds; if (state->saveIN != SHIN) xclose(SHIN); if (state->saveOUT != SHOUT) xclose(SHOUT); if (state->saveDIAG != SHDIAG) xclose(SHDIAG); close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1); close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1); close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1); if (didfds) { close_on_exec(dcopy(SHIN, 0), 1); close_on_exec(dcopy(SHOUT, 1), 1); close_on_exec(dcopy(SHDIAG, 2), 1); } } static Char **Ggv; /*ARGSUSED*/ void doeval(Char **v, struct command *c) { struct doeval_state state; int gflag, my_reenter; Char **gv; jmp_buf_t osetexit; USE(c); v++; if (*v == 0) return; gflag = tglob(v); if (gflag) { gv = v = globall(v, gflag); if (v == 0) stderror(ERR_NOMATCH); cleanup_push(gv, blk_cleanup); v = copyblk(v); } else { gv = NULL; v = copyblk(v); trim(v); } Ggv = gv; state.evalvec = evalvec; state.evalp = evalp; state.didfds = didfds; #ifndef CLOSE_ON_EXEC state.didcch = didcch; #endif /* CLOSE_ON_EXEC */ state.SHIN = SHIN; state.SHOUT = SHOUT; state.SHDIAG = SHDIAG; (void)close_on_exec(state.saveIN = dcopy(SHIN, -1), 1); (void)close_on_exec(state.saveOUT = dcopy(SHOUT, -1), 1); (void)close_on_exec(state.saveDIAG = dcopy(SHDIAG, -1), 1); cleanup_push(&state, doeval_cleanup); getexit(osetexit); /* PWP: setjmp/longjmp bugfix for optimizing compilers */ #ifdef cray my_reenter = 1; /* assume non-zero return val */ if (setexit() == 0) { my_reenter = 0; /* Oh well, we were wrong */ #else /* !cray */ if ((my_reenter = setexit()) == 0) { #endif /* cray */ evalvec = v; evalp = 0; (void)close_on_exec(SHIN = dcopy(0, -1), 1); (void)close_on_exec(SHOUT = dcopy(1, -1), 1); (void)close_on_exec(SHDIAG = dcopy(2, -1), 1); #ifndef CLOSE_ON_EXEC didcch = 0; #endif /* CLOSE_ON_EXEC */ didfds = 0; gv = Ggv; process(0); Ggv = gv; } if (my_reenter == 0) { cleanup_until(&state); if (Ggv) cleanup_until(Ggv); } resexit(osetexit); if (my_reenter) stderror(ERR_SILENT); } /*************************************************************************/ /* print list of builtin commands */ static void lbuffed_cleanup (void *dummy) { USE(dummy); lbuffed = 1; } /*ARGSUSED*/ void dobuiltins(Char **v, struct command *c) { /* would use print_by_column() in tw.parse.c but that assumes * we have an array of Char * to pass.. (sg) */ const struct biltins *b; int row, col, columns, rows; unsigned int w, maxwidth; USE(c); USE(v); lbuffed = 0; /* turn off line buffering */ cleanup_push(&lbuffed, lbuffed_cleanup); /* find widest string */ for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b) maxwidth = max(maxwidth, strlen(b->bname)); ++maxwidth; /* for space */ columns = (TermH + 1) / maxwidth; /* PWP: terminal size change */ if (!columns) columns = 1; rows = (nbfunc + (columns - 1)) / columns; for (b = bfunc, row = 0; row < rows; row++) { for (col = 0; col < columns; col++) { if (b < &bfunc[nbfunc]) { w = strlen(b->bname); xprintf("%s", b->bname); if (col < (columns - 1)) /* Not last column? */ for (; w < maxwidth; w++) xputchar(' '); ++b; } } if (row < (rows - 1)) { if (Tty_raw_mode) xputchar('\r'); xputchar('\n'); } } #ifdef WINNT_NATIVE nt_print_builtins(maxwidth); #else if (Tty_raw_mode) xputchar('\r'); xputchar('\n'); #endif /* WINNT_NATIVE */ cleanup_until(&lbuffed); /* turn back on line buffering */ flush(); } #ifdef NLS_CATALOGS char * xcatgets(nl_catd ctd, int set_id, int msg_id, const char *s) { char *res; errno = 0; while ((res = catgets(ctd, set_id, msg_id, s)) == s && errno == EINTR) { handle_pending_signals(); errno = 0; } return res; } # if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) char * iconv_catgets(nl_catd ctd, int set_id, int msg_id, const char *s) { static char *buf = NULL; static size_t buf_size = 0; char *orig, *dest, *p; ICONV_CONST char *src; size_t src_size, dest_size; orig = xcatgets(ctd, set_id, msg_id, s); if (catgets_iconv == (iconv_t)-1 || orig == s) return orig; src = orig; src_size = strlen(src) + 1; if (buf == NULL && (buf = xmalloc(buf_size = src_size + 32)) == NULL) return orig; dest = buf; while (src_size != 0) { dest_size = buf + buf_size - dest; if (iconv(catgets_iconv, &src, &src_size, &dest, &dest_size) == (size_t)-1) { switch (errno) { case E2BIG: if ((p = xrealloc(buf, buf_size * 2)) == NULL) return orig; buf_size *= 2; dest = p + (dest - buf); buf = p; break; case EILSEQ: case EINVAL: default: return orig; } } } return buf; } # endif /* HAVE_ICONV && HAVE_NL_LANGINFO */ #endif /* NLS_CATALOGS */ void nlsinit(void) { #ifdef NLS_CATALOGS static const char default_catalog[] = "tcsh"; char *catalog = (char *)(intptr_t)default_catalog; if (adrof(STRcatalog) != NULL) catalog = xasprintf("tcsh.%s", short2str(varval(STRcatalog))); #ifdef NL_CAT_LOCALE /* POSIX-compliant. */ /* * Check if LC_MESSAGES is set in the environment and use it, if so. * If not, fall back to the setting of LANG. */ catd = catopen(catalog, tgetenv(STRLC_MESSAGES) ? NL_CAT_LOCALE : 0); #else /* pre-POSIX */ # ifndef MCLoadBySet # define MCLoadBySet 0 # endif catd = catopen(catalog, MCLoadBySet); #endif if (catalog != default_catalog) xfree(catalog); #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) /* xcatgets (), not CGETS, the charset name should be in ASCII anyway. */ catgets_iconv = iconv_open (nl_langinfo (CODESET), xcatgets(catd, 255, 1, "UTF-8")); #endif /* HAVE_ICONV && HAVE_NL_LANGINFO */ #endif /* NLS_CATALOGS */ #ifdef WINNT_NATIVE nls_dll_init(); #endif /* WINNT_NATIVE */ errinit(); /* init the errorlist in correct locale */ mesginit(); /* init the messages for signals */ dateinit(); /* init the messages for dates */ editinit(); /* init the editor messages */ terminit(); /* init the termcap messages */ } void nlsclose(void) { #ifdef NLS_CATALOGS #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) if (catgets_iconv != (iconv_t)-1) { iconv_close(catgets_iconv); catgets_iconv = (iconv_t)-1; } #endif /* HAVE_ICONV && HAVE_NL_LANGINFO */ if (catd != (nl_catd)-1) { /* * catclose can call other functions which can call longjmp * making us re-enter this code. Prevent infinite recursion * by resetting catd. Problem reported and solved by: * Gerhard Niklasch */ nl_catd oldcatd = catd; catd = (nl_catd)-1; while (catclose(oldcatd) == -1 && errno == EINTR) handle_pending_signals(); } #endif /* NLS_CATALOGS */ } int getYN(const char *prompt) { - int doit, c; + int doit; + char c; + xprintf("%s", prompt); flush(); - (void) force_read(SHIN, &c, 1); + (void) force_read(SHIN, &c, sizeof(c)); /* * Perhaps we should use the yesexpr from the * actual locale */ doit = (strchr(CGETS(22, 14, "Yy"), c) != NULL); - while (c != '\n' && force_read(SHIN, &c, 1) == 1) + while (c != '\n' && force_read(SHIN, &c, sizeof(c)) == sizeof(c)) continue; return doit; } Index: head/contrib/tcsh/tw.init.c =================================================================== --- head/contrib/tcsh/tw.init.c (revision 315951) +++ head/contrib/tcsh/tw.init.c (revision 315952) @@ -1,1031 +1,1030 @@ /* $Header: /p/tcsh/cvsroot/tcsh/tw.init.c,v 3.42 2011/04/17 14:49:30 christos Exp $ */ /* * tw.init.c: Handle lists of things to complete */ /*- * Copyright (c) 1980, 1991 The 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. 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 REGENTS 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. */ #include "sh.h" RCSID("$tcsh: tw.init.c,v 3.42 2011/04/17 14:49:30 christos Exp $") #include "tw.h" #include "ed.h" #include "tc.h" #include "sh.proc.h" #define TW_INCR 128 typedef struct { Char **list, /* List of command names */ *buff; /* Space holding command names */ size_t nlist, /* Number of items */ nbuff, /* Current space in name buf */ tlist, /* Total space in list */ tbuff; /* Total space in name buf */ } stringlist_t; static struct varent *tw_vptr = NULL; /* Current shell variable */ static Char **tw_env = NULL; /* Current environment variable */ static const Char *tw_word; /* Current word pointer */ static struct KeyFuncs *tw_bind = NULL; /* List of the bindings */ #ifndef HAVENOLIMIT static struct limits *tw_limit = NULL; /* List of the resource limits */ #endif /* HAVENOLIMIT */ static int tw_index = 0; /* signal and job index */ static DIR *tw_dir_fd = NULL; /* Current directory descriptor */ static int tw_cmd_got = 0; /* What we need to do */ static stringlist_t tw_cmd = { NULL, NULL, 0, 0, 0, 0 }; static stringlist_t tw_item = { NULL, NULL, 0, 0, 0, 0 }; #define TW_FL_CMD 0x01 #define TW_FL_ALIAS 0x02 #define TW_FL_BUILTIN 0x04 #define TW_FL_SORT 0x08 #define TW_FL_REL 0x10 static struct { /* Current element pointer */ size_t cur; /* Current element number */ Char **pathv; /* Current element in path */ DIR *dfd; /* Current directory descriptor */ } tw_cmd_state; #define SETDIR(dfd) \ { \ tw_dir_fd = dfd; \ if (tw_dir_fd != NULL) \ rewinddir(tw_dir_fd); \ } #define CLRDIR(dfd) \ if (dfd != NULL) { \ pintr_disabled++; \ xclosedir(dfd); \ dfd = NULL; \ disabled_cleanup(&pintr_disabled); \ } static Char *tw_str_add (stringlist_t *, size_t); static void tw_str_free (stringlist_t *); static int tw_dir_next (struct Strbuf *, DIR *); static void tw_cmd_add (const Char *name); static void tw_cmd_cmd (void); static void tw_cmd_builtin (void); static void tw_cmd_alias (void); static void tw_cmd_sort (void); static void tw_vptr_start (struct varent *); /* tw_str_add(): * Add an item to the string list */ static Char * tw_str_add(stringlist_t *sl, size_t len) { Char *ptr; if (sl->tlist <= sl->nlist) { pintr_disabled++; sl->tlist += TW_INCR; sl->list = xrealloc(sl->list, sl->tlist * sizeof(Char *)); disabled_cleanup(&pintr_disabled); } if (sl->tbuff <= sl->nbuff + len) { size_t i; ptr = sl->buff; pintr_disabled++; sl->tbuff += TW_INCR + len; sl->buff = xrealloc(sl->buff, sl->tbuff * sizeof(Char)); /* Re-thread the new pointer list, if changed */ if (ptr != NULL && ptr != sl->buff) { - intptr_t offs = sl->buff - ptr; for (i = 0; i < sl->nlist; i++) - sl->list[i] += offs; + sl->list[i] = sl->buff + (sl->list[i] - ptr); } disabled_cleanup(&pintr_disabled); } ptr = sl->list[sl->nlist++] = &sl->buff[sl->nbuff]; sl->nbuff += len; return ptr; } /* tw_str_add */ /* tw_str_free(): * Free a stringlist */ static void tw_str_free(stringlist_t *sl) { pintr_disabled++; if (sl->list) { xfree(sl->list); sl->list = NULL; sl->tlist = sl->nlist = 0; } if (sl->buff) { xfree(sl->buff); sl->buff = NULL; sl->tbuff = sl->nbuff = 0; } disabled_cleanup(&pintr_disabled); } /* end tw_str_free */ static int tw_dir_next(struct Strbuf *res, DIR *dfd) { struct dirent *dirp; if (dfd == NULL) return 0; if ((dirp = readdir(dfd)) != NULL) { Strbuf_append(res, str2short(dirp->d_name)); return 1; } return 0; } /* end tw_dir_next */ /* tw_cmd_add(): * Add the name to the command list */ static void tw_cmd_add(const Char *name) { size_t len; len = Strlen(name) + 2; (void) Strcpy(tw_str_add(&tw_cmd, len), name); } /* end tw_cmd_add */ /* tw_cmd_free(): * Free the command list */ void tw_cmd_free(void) { CLRDIR(tw_dir_fd) tw_str_free(&tw_cmd); tw_cmd_got = 0; } /* end tw_cmd_free */ /* tw_cmd_cmd(): * Add system commands to the command list */ static void tw_cmd_cmd(void) { DIR *dirp; struct dirent *dp; Char *dir = NULL, *name; Char **pv; struct varent *v = adrof(STRpath); struct varent *recexec = adrof(STRrecognize_only_executables); size_t len; if (v == NULL || v->vec == NULL) /* if no path */ return; for (pv = v->vec; *pv; pv++) { if (pv[0][0] != '/') { tw_cmd_got |= TW_FL_REL; continue; } if ((dirp = opendir(short2str(*pv))) == NULL) continue; cleanup_push(dirp, opendir_cleanup); if (recexec) { dir = Strspl(*pv, STRslash); cleanup_push(dir, xfree); } while ((dp = readdir(dirp)) != NULL) { #if defined(_UWIN) || defined(__CYGWIN__) /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns * the file with the .exe, .com, .bat extension * * Same for Cygwin, but only for .exe and .com extension. */ len = strlen(dp->d_name); if (len > 4 && (strcmp(&dp->d_name[len - 4], ".exe") == 0 || #ifndef __CYGWIN__ strcmp(&dp->d_name[len - 4], ".bat") == 0 || #endif /* !__CYGWIN__ */ strcmp(&dp->d_name[len - 4], ".com") == 0)) dp->d_name[len - 4] = '\0'; #endif /* _UWIN || __CYGWIN__ */ /* the call to executable() may make this a bit slow */ name = str2short(dp->d_name); if (dp->d_ino == 0 || (recexec && !executable(dir, name, 0))) continue; len = Strlen(name); if (name[0] == '#' || /* emacs temp files */ name[0] == '.' || /* .files */ name[len - 1] == '~' || /* emacs backups */ name[len - 1] == '%') /* textedit backups */ continue; /* Ignore! */ tw_cmd_add(name); } cleanup_until(dirp); } } /* end tw_cmd_cmd */ /* tw_cmd_builtin(): * Add builtins to the command list */ static void tw_cmd_builtin(void) { const struct biltins *bptr; for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) if (bptr->bname) tw_cmd_add(str2short(bptr->bname)); #ifdef WINNT_NATIVE for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) if (bptr->bname) tw_cmd_add(str2short(bptr->bname)); #endif /* WINNT_NATIVE*/ } /* end tw_cmd_builtin */ /* tw_cmd_alias(): * Add aliases to the command list */ static void tw_cmd_alias(void) { struct varent *p; struct varent *c; p = &aliases; for (;;) { while (p->v_left) p = p->v_left; x: if (p->v_parent == 0) /* is it the header? */ return; if (p->v_name) tw_cmd_add(p->v_name); if (p->v_right) { p = p->v_right; continue; } do { c = p; p = p->v_parent; } while (p->v_right == c); goto x; } } /* end tw_cmd_alias */ /* tw_cmd_sort(): * Sort the command list removing duplicate elements */ static void tw_cmd_sort(void) { size_t fwd, i; pintr_disabled++; /* sort the list. */ qsort(tw_cmd.list, tw_cmd.nlist, sizeof(Char *), fcompare); /* get rid of multiple entries */ for (i = 0, fwd = 0; i + 1 < tw_cmd.nlist; i++) { if (Strcmp(tw_cmd.list[i], tw_cmd.list[i + 1]) == 0) /* garbage */ fwd++; /* increase the forward ref. count */ else if (fwd) tw_cmd.list[i - fwd] = tw_cmd.list[i]; } /* Fix fencepost error -- Theodore Ts'o */ if (fwd) tw_cmd.list[i - fwd] = tw_cmd.list[i]; tw_cmd.nlist -= fwd; disabled_cleanup(&pintr_disabled); } /* end tw_cmd_sort */ /* tw_cmd_start(): * Get the command list and sort it, if not done yet. * Reset the current pointer to the beginning of the command list */ /*ARGSUSED*/ void tw_cmd_start(DIR *dfd, const Char *pat) { static Char *defpath[] = { STRNULL, 0 }; USE(pat); SETDIR(dfd) if ((tw_cmd_got & TW_FL_CMD) == 0) { tw_cmd_free(); tw_cmd_cmd(); tw_cmd_got |= TW_FL_CMD; } if ((tw_cmd_got & TW_FL_ALIAS) == 0) { tw_cmd_alias(); tw_cmd_got &= ~TW_FL_SORT; tw_cmd_got |= TW_FL_ALIAS; } if ((tw_cmd_got & TW_FL_BUILTIN) == 0) { tw_cmd_builtin(); tw_cmd_got &= ~TW_FL_SORT; tw_cmd_got |= TW_FL_BUILTIN; } if ((tw_cmd_got & TW_FL_SORT) == 0) { tw_cmd_sort(); tw_cmd_got |= TW_FL_SORT; } tw_cmd_state.cur = 0; CLRDIR(tw_cmd_state.dfd) if (tw_cmd_got & TW_FL_REL) { struct varent *vp = adrof(STRpath); if (vp && vp->vec) tw_cmd_state.pathv = vp->vec; else tw_cmd_state.pathv = defpath; } else tw_cmd_state.pathv = defpath; } /* tw_cmd_start */ /* tw_cmd_next(): * Return the next element in the command list or * Look for commands in the relative path components */ int tw_cmd_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { int ret = 0; Char *ptr; if (tw_cmd_state.cur < tw_cmd.nlist) { *flags = TW_DIR_OK; Strbuf_append(res, tw_cmd.list[tw_cmd_state.cur++]); return 1; } /* * We need to process relatives in the path. */ while ((tw_cmd_state.dfd == NULL || (res->len = 0, ret = tw_dir_next(res, tw_cmd_state.dfd)) == 0) && *tw_cmd_state.pathv != NULL) { CLRDIR(tw_cmd_state.dfd) while (*tw_cmd_state.pathv && tw_cmd_state.pathv[0][0] == '/') tw_cmd_state.pathv++; if ((ptr = *tw_cmd_state.pathv) != 0) { res->len = 0; Strbuf_append(res, ptr); ret = 1; /* * We complete directories only on '.' should that * be changed? */ dir->len = 0; if (ptr[0] == '\0' || (ptr[0] == '.' && ptr[1] == '\0')) { tw_cmd_state.dfd = opendir("."); *flags = TW_DIR_OK | TW_EXEC_CHK; } else { Strbuf_append(dir, *tw_cmd_state.pathv); Strbuf_append1(dir, '/'); tw_cmd_state.dfd = opendir(short2str(*tw_cmd_state.pathv)); *flags = TW_EXEC_CHK; } Strbuf_terminate(dir); tw_cmd_state.pathv++; } } return ret; } /* end tw_cmd_next */ /* tw_vptr_start(): * Find the first variable in the variable list */ static void tw_vptr_start(struct varent *c) { tw_vptr = c; /* start at beginning of variable list */ for (;;) { while (tw_vptr->v_left) tw_vptr = tw_vptr->v_left; x: if (tw_vptr->v_parent == 0) { /* is it the header? */ tw_vptr = NULL; return; } if (tw_vptr->v_name) return; /* found first one */ if (tw_vptr->v_right) { tw_vptr = tw_vptr->v_right; continue; } do { c = tw_vptr; tw_vptr = tw_vptr->v_parent; } while (tw_vptr->v_right == c); goto x; } } /* end tw_shvar_start */ /* tw_shvar_next(): * Return the next shell variable */ /*ARGSUSED*/ int tw_shvar_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { struct varent *p; struct varent *c; USE(flags); USE(dir); if ((p = tw_vptr) == NULL) return 0; /* just in case */ Strbuf_append(res, p->v_name); /* we know that this name is here now */ /* now find the next one */ for (;;) { if (p->v_right) { /* if we can go right */ p = p->v_right; while (p->v_left) p = p->v_left; } else { /* else go up */ do { c = p; p = p->v_parent; } while (p->v_right == c); } if (p->v_parent == 0) { /* is it the header? */ tw_vptr = NULL; return 1; } if (p->v_name) { tw_vptr = p; /* save state for the next call */ return 1; } } } /* end tw_shvar_next */ /* tw_envvar_next(): * Return the next environment variable */ /*ARGSUSED*/ int tw_envvar_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { const Char *ps; USE(flags); USE(dir); if (tw_env == NULL || *tw_env == NULL) return 0; for (ps = *tw_env; *ps && *ps != '='; ps++) continue; Strbuf_appendn(res, *tw_env, ps - *tw_env); tw_env++; return 1; } /* end tw_envvar_next */ /* tw_var_start(): * Begin the list of the shell and environment variables */ /*ARGSUSED*/ void tw_var_start(DIR *dfd, const Char *pat) { USE(pat); SETDIR(dfd) tw_vptr_start(&shvhed); tw_env = STR_environ; } /* end tw_var_start */ /* tw_alias_start(): * Begin the list of the shell aliases */ /*ARGSUSED*/ void tw_alias_start(DIR *dfd, const Char *pat) { USE(pat); SETDIR(dfd) tw_vptr_start(&aliases); tw_env = NULL; } /* tw_alias_start */ /* tw_complete_start(): * Begin the list of completions */ /*ARGSUSED*/ void tw_complete_start(DIR *dfd, const Char *pat) { USE(pat); SETDIR(dfd) tw_vptr_start(&completions); tw_env = NULL; } /* end tw_complete_start */ /* tw_var_next(): * Return the next shell or environment variable */ int tw_var_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { int ret = 0; if (tw_vptr) ret = tw_shvar_next(res, dir, flags); if (ret == 0 && tw_env) ret = tw_envvar_next(res, dir, flags); return ret; } /* end tw_var_next */ /* tw_logname_start(): * Initialize lognames to the beginning of the list */ /*ARGSUSED*/ void tw_logname_start(DIR *dfd, const Char *pat) { USE(pat); SETDIR(dfd) #ifdef HAVE_GETPWENT (void) setpwent(); /* Open passwd file */ #endif } /* end tw_logname_start */ /* tw_logname_next(): * Return the next entry from the passwd file */ /*ARGSUSED*/ int tw_logname_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { struct passwd *pw; /* * We don't want to get interrupted inside getpwent() * because the yellow pages code is not interruptible, * and if we call endpwent() immediatetely after * (in pintr()) we may be freeing an invalid pointer */ USE(flags); USE(dir); pintr_disabled++; #ifdef HAVE_GETPWENT pw = getpwent(); #else pw = NULL; #endif disabled_cleanup(&pintr_disabled); if (pw == NULL) { #ifdef YPBUGS fix_yp_bugs(); #endif return 0; } Strbuf_append(res, str2short(pw->pw_name)); return 1; } /* end tw_logname_next */ /* tw_logname_end(): * Close the passwd file to finish the logname list */ void tw_logname_end(void) { #ifdef YPBUGS fix_yp_bugs(); #endif #ifdef HAVE_GETPWENT (void) endpwent(); #endif } /* end tw_logname_end */ /* tw_grpname_start(): * Initialize grpnames to the beginning of the list */ /*ARGSUSED*/ void tw_grpname_start(DIR *dfd, const Char *pat) { USE(pat); SETDIR(dfd) #if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined (__ANDROID__) (void) setgrent(); /* Open group file */ #endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */ } /* end tw_grpname_start */ /* tw_grpname_next(): * Return the next entry from the group file */ /*ARGSUSED*/ int tw_grpname_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { struct group *gr; /* * We don't want to get interrupted inside getgrent() * because the yellow pages code is not interruptible, * and if we call endgrent() immediatetely after * (in pintr()) we may be freeing an invalid pointer */ USE(flags); USE(dir); pintr_disabled++; #if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined(__ANDROID__) errno = 0; while ((gr = getgrent()) == NULL && errno == EINTR) { handle_pending_signals(); errno = 0; } #else /* _VMS_POSIX || _OSD_POSIX || WINNT_NATIVE */ gr = NULL; #endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */ disabled_cleanup(&pintr_disabled); if (gr == NULL) { #ifdef YPBUGS fix_yp_bugs(); #endif return 0; } Strbuf_append(res, str2short(gr->gr_name)); return 1; } /* end tw_grpname_next */ /* tw_grpname_end(): * Close the group file to finish the groupname list */ void tw_grpname_end(void) { #ifdef YPBUGS fix_yp_bugs(); #endif #if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined (__ANDROID__) (void) endgrent(); #endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */ } /* end tw_grpname_end */ /* tw_file_start(): * Initialize the directory for the file list */ /*ARGSUSED*/ void tw_file_start(DIR *dfd, const Char *pat) { struct varent *vp; USE(pat); SETDIR(dfd) if ((vp = adrof(STRcdpath)) != NULL) tw_env = vp->vec; } /* end tw_file_start */ /* tw_file_next(): * Return the next file in the directory */ int tw_file_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { int ret = tw_dir_next(res, tw_dir_fd); if (ret == 0 && (*flags & TW_DIR_OK) != 0) { CLRDIR(tw_dir_fd) while (tw_env && *tw_env) if ((tw_dir_fd = opendir(short2str(*tw_env))) != NULL) break; else tw_env++; if (tw_dir_fd) { dir->len = 0; Strbuf_append(dir, *tw_env++); Strbuf_append1(dir, '/'); Strbuf_terminate(dir); ret = tw_dir_next(res, tw_dir_fd); } } return ret; } /* end tw_file_next */ /* tw_dir_end(): * Clear directory related lists */ void tw_dir_end(void) { CLRDIR(tw_dir_fd) CLRDIR(tw_cmd_state.dfd) } /* end tw_dir_end */ /* tw_item_free(): * Free the item list */ void tw_item_free(void) { tw_str_free(&tw_item); } /* end tw_item_free */ /* tw_item_get(): * Return the list of items */ Char ** tw_item_get(void) { return tw_item.list; } /* end tw_item_get */ /* tw_item_add(): * Return a new item for a Strbuf_terminate()'d s */ void tw_item_add(const struct Strbuf *s) { Char *p; p = tw_str_add(&tw_item, s->len + 1); Strcpy(p, s->s); } /* tw_item_add */ /* tw_item_find(): * Find the string if it exists in the item list * end return it. */ Char * tw_item_find(Char *str) { size_t i; if (tw_item.list == NULL || str == NULL) return NULL; for (i = 0; i < tw_item.nlist; i++) if (tw_item.list[i] != NULL && Strcmp(tw_item.list[i], str) == 0) return tw_item.list[i]; return NULL; } /* end tw_item_find */ /* tw_vl_start(): * Initialize a variable list */ void tw_vl_start(DIR *dfd, const Char *pat) { SETDIR(dfd) if ((tw_vptr = adrof(pat)) != NULL) { tw_env = tw_vptr->vec; tw_vptr = NULL; } else tw_env = NULL; } /* end tw_vl_start */ /* * Initialize a word list */ void tw_wl_start(DIR *dfd, const Char *pat) { SETDIR(dfd); tw_word = pat; } /* end tw_wl_start */ /* * Return the next word from the word list */ /*ARGSUSED*/ int tw_wl_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { const Char *p; USE(dir); USE(flags); if (tw_word == NULL || tw_word[0] == '\0') return 0; while (*tw_word && Isspace(*tw_word)) tw_word++; for (p = tw_word; *tw_word && !Isspace(*tw_word); tw_word++) continue; if (tw_word == p) return 0; Strbuf_appendn(res, p, tw_word - p); if (*tw_word) tw_word++; return 1; } /* end tw_wl_next */ /* tw_bind_start(): * Begin the list of the shell bindings */ /*ARGSUSED*/ void tw_bind_start(DIR *dfd, const Char *pat) { USE(pat); SETDIR(dfd) tw_bind = FuncNames; } /* end tw_bind_start */ /* tw_bind_next(): * Begin the list of the shell bindings */ /*ARGSUSED*/ int tw_bind_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { USE(dir); USE(flags); if (tw_bind && tw_bind->name) { const char *ptr; for (ptr = tw_bind->name; *ptr != '\0'; ptr++) Strbuf_append1(res, *ptr); tw_bind++; return 1; } return 0; } /* end tw_bind_next */ /* tw_limit_start(): * Begin the list of the shell limitings */ /*ARGSUSED*/ void tw_limit_start(DIR *dfd, const Char *pat) { USE(pat); SETDIR(dfd) #ifndef HAVENOLIMIT tw_limit = limits; #endif /* ! HAVENOLIMIT */ } /* end tw_limit_start */ /* tw_limit_next(): * Begin the list of the shell limitings */ /*ARGSUSED*/ int tw_limit_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { USE(dir); USE(flags); #ifndef HAVENOLIMIT if (tw_limit && tw_limit->limname) { const char *ptr; for (ptr = tw_limit->limname; *ptr != '\0'; ptr++) Strbuf_append1(res, *ptr); tw_limit++; return 1; } #endif /* ! HAVENOLIMIT */ return 0; } /* end tw_limit_next */ /* tw_sig_start(): * Begin the list of the shell sigings */ /*ARGSUSED*/ void tw_sig_start(DIR *dfd, const Char *pat) { USE(pat); SETDIR(dfd) tw_index = 0; } /* end tw_sig_start */ /* tw_sig_next(): * Begin the list of the shell sigings */ /*ARGSUSED*/ int tw_sig_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { USE(dir); USE(flags); for (;tw_index < nsig; tw_index++) { const char *ptr; if (mesg[tw_index].iname == NULL) continue; for (ptr = mesg[tw_index].iname; *ptr != '\0'; ptr++) Strbuf_append1(res, *ptr); tw_index++; return 1; } return 0; } /* end tw_sig_next */ /* tw_job_start(): * Begin the list of the shell jobings */ /*ARGSUSED*/ void tw_job_start(DIR *dfd, const Char *pat) { USE(pat); SETDIR(dfd) tw_index = 1; } /* end tw_job_start */ /* tw_job_next(): * Begin the list of the shell jobings */ /*ARGSUSED*/ int tw_job_next(struct Strbuf *res, struct Strbuf *dir, int *flags) { struct process *j; USE(dir); USE(flags); for (;tw_index <= pmaxindex; tw_index++) { for (j = proclist.p_next; j != NULL; j = j->p_next) if (j->p_index == tw_index && j->p_procid == j->p_jobid) break; if (j == NULL) continue; Strbuf_append(res, j->p_command); tw_index++; return 1; } return 0; } /* end tw_job_next */ Index: head/contrib/tcsh =================================================================== --- head/contrib/tcsh (revision 315951) +++ head/contrib/tcsh (revision 315952) Property changes on: head/contrib/tcsh ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /vendor/tcsh/dist:r315950