home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / shells / bashsrc.zoo / builtins.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-06-05  |  96.4 KB  |  3,732 lines

  1. /* builtins.c -- the built in shell commands. */
  2.  
  3. /* Copyright (C) 1987,1989 Free Software Foundation, Inc.
  4.  
  5. This file is part of GNU Bash, the Bourne Again SHell.
  6.  
  7. Bash is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 1, or (at your option) any later
  10. version.
  11.  
  12. Bash is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with Bash; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include <stdio.h>
  22. #include <sys/param.h>
  23.  
  24. #include <sys/types.h>
  25. #include <sys/stat.h>
  26. #include <sys/file.h>
  27. #include <errno.h>
  28. #include "shell.h"
  29.  
  30. #ifndef SYSV
  31. #include <sys/time.h>
  32. #include <sys/resource.h>
  33. #endif
  34.  
  35. #include "builtins.h"
  36. #include "trap.h"
  37. #include "flags.h"
  38. #include <readline/history.h>
  39.  
  40. #ifdef JOB_CONTROL
  41. #include "jobs.h"
  42. #endif
  43.  
  44. extern int errno;        /* Not always in <errno.h>.  Bogusness. */
  45.  
  46. #ifndef sigmask
  47. #define sigmask(x) (1 << ((x)-1))
  48. #endif
  49.  
  50. #ifdef SYSV
  51. #include <fcntl.h>
  52. #include <sys/times.h>
  53. #endif
  54.  
  55. #if defined (HAVE_VPRINTF)
  56. #include <varargs.h>
  57. #endif
  58.  
  59. /* Yecch!  Who cares about this gross concept in the first place? */
  60. #ifndef MAXPATHLEN
  61. #define MAXPATHLEN 1024
  62. #endif
  63.  
  64. /* The command name of the currently running function. */
  65. extern char *this_command_name;
  66.  
  67. /* Non-zero means running an interactive shell. */
  68. extern int interactive;
  69.  
  70. /* The list of shell builtins.  Each element is name, function, enabled-p,
  71.    short-doc, long-doc.  The long-doc field should contain a set of indented
  72.    lines.  The function takes a WORD_LIST *, where the first word in the list
  73.    is the first arg to the command.  The list has already been word expanded.
  74.  
  75.    Procedures which need to look at every simple command (like enable_builtin),
  76.    should tree-walk looking for (array[i].function == (Function *)NULL).  The
  77.    list of executable builtins (in the shell sense) ends there.  Then comes
  78.    the control structure commands, like `if' and `while'.  */
  79.  
  80. struct builtin shell_builtins[] = {
  81.  
  82.   { ":", colon_builtin, 1, ":",
  83.       "    No effect; the command does nothing.  A zero exit code is returned" },
  84.  
  85.   { ".", period_builtin, 1, ". [filename]",
  86.       "    Read and execute commands from FILENAME and return.\n\
  87.     Pathnames in $PATH are used to find the directory containing FILENAME" },
  88.  
  89.   { "alias", alias_builtin, 1, "alias [ name[=value] ... ]",
  90.       "    Alias with no arguments prints the list of aliases in the form\n\
  91.     name=value on standard output.  An alias is defined for each NAME\n\
  92.     whose VALUE is given.  A trailing space in VALUE causes the next\n\
  93.     word to be checked for alias substitution.  Alias returns true\n\
  94.     unless a NAME is given for which no alias has been defined" },
  95.  
  96. #ifdef JOB_CONTROL
  97.   { "bg", bg_builtin, 1, "bg [job_spec]",
  98.       "    Place JOB_SPEC in the background, as if it had been started with\n\
  99.     `&'.  If JOB_SPEC is not present, the shell's notion of the current\n\
  100.     job is used" },
  101. #endif
  102.  
  103.   { "break", break_builtin, 1, "break [n]",
  104.       "    Exit from within a FOR, WHILE or UNTIL loop.  If N is specified,\n\
  105.     break N levels" },
  106.  
  107.   { "builtin", builtin_builtin, 1, "builtin [shell-builtin [arg ...]]",
  108.       "    Run a shell builtin.  This is useful when you wish to rename a\n\
  109.     shell builtin to be a function, but need the functionality of the\n\
  110.     builtin within the function itself" },
  111.  
  112.   { "bye", exit_builtin, 1, "bye [n]",
  113.       "    Synonym for exit" },
  114.  
  115.   { "cd", cd_builtin, 1, "cd [dir]",
  116.       "    Change the current directory to DIR.  The variable $HOME is the\n\
  117.     default DIR.  The variable $CDPATH defines the search path for\n\
  118.     the directory containing DIR.  Alternative directory names are\n\
  119.     separated by a colon (:).  A null directory name is the same as\n\
  120.     the current directory, i.e. `.'.  If DIR begins with a slash (/),\n\
  121.     then $CDPATH is not used" },
  122.  
  123.   { "command", command_builtin, 1, "command [command [arg ...]]",
  124.       "    Runs COMMAND with ARGS ignoring shell functions.  If you have a\n\
  125.     shell function called `ls', and you wish to call the command\n\
  126.     `ls', you can say \"command ls\"" },
  127.  
  128.   { "continue", continue_builtin, 1, "continue [n]",
  129.       "    Resume the next iteration of the enclosing FOR, WHILE or UNTIL loop.\n\
  130.     If N is specified, resume at the N-th enclosing loop" },
  131.  
  132.   { "declare", declare_builtin, 1, "declare [-[frx]] name[=value]",
  133.       "    Declare variables and/or give them attributes.  If no NAMEs are\n\
  134.     are given, then display the values of variables instead.  `-f'\n\
  135.     says to use function names only.  `-r' says to make NAMEs readonly.\n\
  136.     `-x' says to make NAMEs export.  Using `+' instead of `-' turns off\n\
  137.     the attribute instead.  When used in a function, makes NAMEs local,\n\
  138.     as with the `local' command" },
  139.  
  140. #ifdef PUSHD_AND_POPD
  141.   { "dirs", dirs_builtin, 1, "dirs",
  142.       "    Display the list of currently remembered directories.  Directories\n\
  143.     find their way onto the list with the `pushd' command; you can get\n\
  144.     back up through the list with the `popd' command" },
  145. #endif
  146.  
  147. #ifndef V9_ECHO
  148.   { "echo", echo_builtin, 1, "echo [-n] [arg]",
  149.       " Output the ARGs.  If -n is specified, then suppress trailing\n\
  150.         newline" },
  151. #else
  152.   { "echo", echo_builtin, 1, "echo [-n] [-e] [arg ...]",
  153.       "    Output the ARGs.  if -n is specified, the trailing newline is\n\
  154.     suppressed.  If the -e option is given, interpretation of the\n\
  155.     following backslash-escaped characters is turned on:\n\
  156.         \\b     backspace\n\
  157.         \\c     suppress trailing newline\n\
  158.         \\f     form feed\n\
  159.         \\n     new line\n\
  160.         \\r     carriage return\n\
  161.         \\t     horizontal tab\n\
  162.         \\v     vertical tab\n\
  163.         \\num   the character whose ASCII code is NUM (octal)"},
  164. #endif
  165.  
  166.   { "enable", enable_builtin, 1, "enable [-n] [name ...]",
  167.       "    Enable and disable builtin shell commands.  This allows\n\
  168.     you to use a disk command which has the same name as a shell\n\
  169.     builtin.  If -n is used, the NAMEs become disabled.  Otherwise\n\
  170.     NAMEs are enabled.  For example, to use the `test' found on your\n\
  171.     path instead of the shell builtin version, you type `enable -n test'" },
  172.  
  173.   { "eval", eval_builtin, 1, "eval [arg ...]",
  174.       "    Read ARGs as input to the shell and execute the resulting command(s)" },
  175.  
  176.   { "exec", exec_builtin, 1, "exec [ [-] file [redirections]]",
  177.       "    Exec FILE, replacing this shell with the specified program.\n\
  178.     If FILE is not specified, the redirections take effect in this\n\
  179.     shell.  If the first argument is `-', then place a dash in the\n\
  180.     zeroith arg passed to FILE.  This is what login does.  If the file\n\
  181.     cannot be exec'ed for some reason, the shell exits, unless the\n\
  182.     shell variable \"no_exit_on_failed_exec\" exists" },
  183.  
  184.   { "exit", exit_builtin, 1, "exit [n]",
  185.       "    Exit the shell with a status of N.  If N is omitted, the exit status\n\
  186.     is that of the last command executed" },
  187.  
  188.   { "export", export_builtin, 1, "export [-n] [name] ...",
  189.       "    NAMEs are marked for automatic export to the environment of\n\
  190.     subsequently executed commands.  If no NAMEs are given, a list\n\
  191.     of all names that are exported in this shell is printed.  Note\n\
  192.     that function names cannot be exported.  An argument of `-n' says\n\
  193.     to remove the export property from subsequent NAMEs" },
  194.  
  195. #ifdef JOB_CONTROL
  196.   { "fg", fg_builtin, 1, "fg [job_spec]",
  197.       "    Place JOB_SPEC in the foreground, and make it the current job.  If\n\
  198.     JOB_SPEC is not present, the shell's notion of the current job is\n\
  199.     used" },
  200. #endif
  201.  
  202.   { "hash", hash_builtin, 1, "hash [-r] [name]",
  203.       "    For each NAME, the full pathname of the command is determined\n\
  204.     and remembered.  The -r option causes the shell to forget all\n\
  205.     remembered locations.  If no arguments are given, information\n\
  206.     about remembered commands is presented" },
  207.  
  208.   { "help", help_builtin, 1, "help [pattern]",
  209.      "    Display helpful information about builtin commands.  If\n\
  210.     PATTERN is specified, gives detailed help on all commands\n\
  211.     matching PATTERN, otherwise a list of the builtins is\n\
  212.     printed" },
  213.  
  214.   { "history", history_builtin, 1, "history [n] [-s] [ [-w | -r] [filename]]",
  215.       "    Display the history list with line numbers.  Lines listed with\n\
  216.     with a `*' have been modified.  Argument of N says to list only\n\
  217.     the last N lines.  Argument `-w' means write out the current\n\
  218.     history file.  `-r' means to read it instead.  If FILENAME is\n\
  219.     given, then use that file, else if $HISTFILE has a value, use\n\
  220.     that, else use ~/.bash_history.  Argument -s oerforms history\n\
  221.     substitution on the following args" },
  222.  
  223. #ifdef JOB_CONTROL
  224.   { "jobs", jobs_builtin, 1, "jobs [-l]",
  225.       "    Lists the active jobs; given the -l options lists process id's\n\
  226.     in addition to the normal information" },
  227.  
  228.   { "kill", kill_builtin, 1, "kill [-sigspec -l] [pid | job] ...",
  229.       "    Send the processes named by PID (or JOB) the signal SIGSPEC.\n\
  230.     If SIGSPEC is not present, then SIGTERM is assumed.  An argument\n\
  231.     of `-l' lists the signal names.  Kill is a builtin for two reasons;\n\
  232.     it allows job id's to be used instead of pids, and if you run out of\n\
  233.     processes, you can still kill them" },
  234. #endif
  235.  
  236.   { "local", local_builtin, 1, "local name[=value]",
  237.       "    Create a local variable called NAME, and give it VALUE.  LOCAL\n\
  238.     can only be used within a function; it makes the variable NAME\n\
  239.     have a visible scope restricted to that function and its children" },
  240.  
  241.   { "logout", logout_builtin, 1, "logout",
  242.       "    Logout of a login shell" },
  243.  
  244. #ifdef PUSHD_AND_POPD
  245.   { "popd", popd_builtin, 1, "popd [+n | -n]",
  246.       "    Removes entries from the directory stack.  With no arguments,\n\
  247.     removes the top directory from the stack, and cd's to the new\n\
  248.     top directory.\n\
  249. \n\
  250.     +n    removes the Nth entry counting from the left of the list\n\
  251.           shown by `dirs', starting with zero.  For example: `popd +0'\n\
  252.           removes the first directory, `popd +1' the second.\n\
  253. \n\
  254.     -n    removes the Nth entry counting from the right of the list\n\
  255.           shown by `dirs', starting with zero.  For example: `popd -0'\n\
  256.           removes the last directory, `popd -1' the next to last.\n\
  257. \n\
  258.     You can see the directory stack with the `dirs' command.\n\
  259.     If the variable 'pushd_silent' is not set and the popd command\n\
  260.     was successful, a 'dirs' will be performed as well." },
  261.  
  262.   { "pushd", pushd_builtin, 1, "pushd [dir | +n | -n]",
  263.       "    Adds a directory to the top of the directory stack, or rotates\n\
  264.     the stack, making the new top of the stack the current working\n\
  265.     directory.  With no arguments, exchanges the top two directories.\n\
  266. \n\
  267.     +n   Rotates the stack so that the Nth directory (counting\n\
  268.          from the left of the list shown by `dirs') is at the top.\n\
  269. \n\
  270.     -n   Rotates the stack so that the Nth directory (counting\n\
  271.          from the right) is at the top.\n\
  272. \n\
  273.     dir  adds DIR to the directory stack at the top, making it the\n\
  274.          new current working directory.\n\
  275. \n\
  276.     You can see the directory stack with the `dirs' command.\n\
  277.     If the variable 'pushd_silent' is not set and the pushd command\n\
  278.     was successful, a 'dirs' will be performed as well." },
  279. #endif /* PUSHD_AND_POPD */
  280.  
  281.   { "pwd", pwd_builtin, 1, "pwd",
  282.       "    Print the current working directory"},
  283.  
  284.   { "read", read_builtin, 1, "read [name ...]",
  285.       "    One line is read from the standard input, and the first word\n\
  286.     is assigned to the first NAME, the second word to the second NAME,\n\
  287.     etc. with leftover words assigned to the last NAME.  Only the\n\
  288.     characters in $IFS are recognized as word delimiters.  The return\n\
  289.     code is zero, unless end-of-file is encountered" },
  290.  
  291.   { "readonly", readonly_builtin, 1, "readonly [name ...]",
  292.       "    The given NAMEs are marked readonly and the values of these NAMEs\n\
  293.     may not be changed by subsequent assignment.  If no arguments are\n\
  294.     given, a list of all readonly names is printed" },
  295.  
  296.   { "return", return_builtin, 1,  "return [n]",
  297.       "    Causes a function to exit with the return value specified by N.\n\
  298.     If N is omitted, the return status is that of the last command" },
  299.  
  300.   { "set", set_builtin, 1, "set [-aefhkntuvx] [arg ...]]",
  301.       "    -a  Mark variables which are modified or created for export\n\
  302.     -e  Exit immediately if a command exits with a non-zero status\n\
  303.     -f  Disable file name generation (globbing)\n\
  304.     -h  Locate and remember function commands as functions are\n\
  305.         defined.  Functions commands are normally looked up when\n\
  306.         the function is executed)\n\
  307.     -k  All keyword arguments are placed in the environment for a\n\
  308.         comand, not just those that precede the command name\n\
  309.     -n  Read commands but do not execute them\n\
  310.     -t  Exit after reading and executing one command\n\
  311.     -u  Treat unset variables as an error when substituting\n\
  312.     -v  Print shell input lines as they are read\n\
  313.     -x  Print commands and their arguments as they are executed\n\
  314.     -l  Save and restore the binding of the NAME in a FOR command.\n\
  315.     -d  Disable the hashing of commands that are looked up for execution.\n\
  316.         Normally, commands are remembered in a hash table, and once\n\
  317.         found, do not have to be looked up again\n\
  318.     -o  Enable ! style history substitution.  This flag is on by\n\
  319.         by default.\n\
  320. \n\
  321.     Using + rather than - causes these flags to be turned off.  The\n\
  322.     flags can also be used upon invocation of the shell.  The current\n\
  323.     set of flags may be found in $-.  The remaining ARGs are positional\n\
  324.     parameters and are assigned, in order, to $1, $2, .. $9.  If no\n\
  325.     ARGs are given, all shell variables are printed" },
  326.  
  327.   { "shift", shift_builtin, 1, "shift [n]",
  328.       "    The positional parameters from $N+1 ... are renamed to $1 ....  If\n\
  329.     N is not given, it is assumed to be 1" },
  330.  
  331.   { "source", period_builtin, 1, "source <file>",
  332.       " An alias for the `.' builtin" },
  333.  
  334. #ifdef JOB_CONTROL
  335.   { "suspend", suspend_builtin, 1, "suspend [-f]",
  336.       "    Suspend the execution of this shell until it receives a SIGCONT\n\
  337.     signal.  The `-f' if specified says not to complain about this\n\
  338.     being a login shell if it is; just suspend anyway" },
  339. #endif
  340.  
  341.   { "[", test_builtin, 1, "[ args ]",
  342.       "    Synonym for `test'" },
  343.  
  344.   { "test", test_builtin, 1, "test [expr]",
  345.       "    Exits with a status of 0 (trueness) or 1 (falseness) depending on\n\
  346.     the evaluation of EXPR.  Expressions may be unary or binary.  Unary\n\
  347.     expressions are often used to examine the status of a file.  There\n\
  348.     are string operators as well, and numeric comparison operators.\n\
  349. \n\
  350.     File operators:\n\
  351. \n\
  352.     -b FILE        True if file is block special.\n\
  353.     -c FILE        True if file is character special.\n\
  354.     -d FILE        True if file is a directory.\n\
  355.     -ef FILE    True if file is a hard link.\n\
  356.     -f FILE        True if file is a plain file.\n\
  357.     -g FILE        True if file is set-group-id.\n\
  358.     -L FILE        True if file is a symbolic link.\n\
  359.     -k FILE        True if file has its \"sticky\" bit set.\n\
  360.     -p FILE        True if file is a named pipe.\n\
  361.     -r FILE        True if file is readable by you.\n\
  362.     -s FILE        True if file is not empty.\n\
  363.     -S FILE        True if file is a socket.\n\
  364.     -t [FD]        True if FD is opened on a terminal.  If FD\n\
  365.             is omitted, it defaults to 1 (stdout).\n\
  366.     -u FILE        True if the file is set-user-id.\n\
  367.     -w FILE        True if the file is writable by you.\n\
  368.     -x FILE        True if the file is executable by you.\n\
  369.     -O FILE        True if the file is effectively owned by you.\n\
  370.     -G FILE        True if the file is effectively owned by your group.\n\
  371. \n\
  372.     FILE1 -nt FILE2    True if file1 is newer than (according to\n\
  373.             modification date) file2.\n\
  374. \n\
  375.     FILE1 -ot FILE2 True if file1 is older than file2.\n\
  376. \n\
  377.     String operators:\n\
  378. \n\
  379.     -z STRING    True if string is empty.\n\
  380.     -n STRING\n\
  381.    or            True if string is not empty.\n\
  382.     STRING\n\
  383.     STRING1 = STRING2\n\
  384.             True if the strings are equal.\n\
  385.     STRING1 != STRING2\n\
  386.             True if the strings are not equal.\n\
  387. \n\
  388.     Other operators:\n\
  389. \n\
  390.     ! EXPR        True if expr is false.\n\
  391.     EXPR1 -a EXPR2    True if both expr1 AND expr2 are true.\n\
  392.     EXPR1 -o EXPR2    True if either expr1 OR expr2 is true.\n\
  393. \n\
  394.     arg1 OP arg2\n\
  395.     OP is one of -eq, -ne, -lt, -le, -gt, ge.\n\
  396.             Arithmetic binary operators return true if ARG1\n\
  397.             is equal, not-equal, less-than, less-than-or-equal,\n\
  398.             greater-than, or greater-than-or-equal than arg2" },
  399.  
  400.   { "times", times_builtin, 1, "times",
  401.       "    Print the accumulated user and system times for processes run from\n\
  402.     the shell" },
  403.  
  404.   { "trap", trap_builtin, 1, "trap [arg] [signal_spec]",
  405.       "    The command ARG is to be read and executed when the shell receives\n\
  406.     signal(s) SIGNAL_SPEC.  If ARG is absent all specified signals are\n\
  407.     are reset to their original values.  If ARG is the null string this\n\
  408.     signal is ignored by the shell and by the commands it invokes.  If\n\
  409.     SIGNAL_SPEC is ON_EXIT (0) the command ARG is executed on exit from\n\
  410.     the shell.  The trap command with no arguments prints the list of\n\
  411.     commands associated with each signal number.  SIGNAL_SPEC is either\n\
  412.     a signal name in <signal.h>, or a signal number.  The syntax `trap -l'\n\
  413.     prints a list of signal names and their corresponding numbers.\n\
  414.     Note that a signal can be sent to the shell with \"kill -signal $$\"" },
  415.  
  416.   { "type", type_builtin, 1, "type [-all] [-type | -path] [name ...]",
  417.       "    For each NAME, indicate how it would be interpreted if used as a\n\
  418.     command name.\n\
  419. \n\
  420.     If the -type flag is used, returns a single word which is one of\n\
  421.     `alias', `function', `builtin', `file' or `', if NAME is an\n\
  422.     alias, shell function, shell builtin, disk file, or unfound,\n\
  423.     respectively.\n\
  424. \n\
  425.     If the -path flag is used, either returns the name of the disk file\n\
  426.     that would be exec'ed, or nothing if -type wouldn't return `file'.\n\
  427. \n\
  428.     If the -all flag is used, returns all of the places that contain\n\
  429.     an executable named `file'.  This includes aliases and functions,\n\
  430.     if and only if the -path flag is not also used" },
  431.  
  432.   { "typeset", declare_builtin, 1, "typeset [-frx] [name[=word]]",
  433.       "    Obsolete.  See `declare'" },
  434.  
  435.   { "ulimit", ulimit_builtin, 1, "ulimit [-cdmstf [limit]]",
  436.       "    Ulimit provides control over the resources available to processes\n\
  437.         started by the shell, on systems that allow such control.  If an\n\
  438.         option is given, it is interpreted as follows:\n\
  439. \n\
  440.                 -c      the maximum size of core files created\n\
  441.                 -d      the maximum size of a process's data segment\n\
  442.                 -m      the maximum resident set size\n\
  443.                 -s      the maximum stack size\n\
  444.                 -t      the maximum amount of cpu time in seconds\n\
  445.                 -f      the maximum size of files created by the shell\n\
  446. \n\
  447.         If LIMIT is given, it is the new value of the specified resource.\n\
  448.     Otherwise, the current value of the specified resource is printed.\n\
  449.     If no option is given, then -f is assumed.  Values are in 1k\n\
  450.     increments, except for -t, which is in seconds" },
  451.  
  452.   { "umask", umask_builtin, 1, "umask [nnn]",
  453.       "    The user file-creation mask is set to NNN.  If NNN is omitted, the\n\
  454.     current value of the mask is printed.  NNN is read as an octal\n\
  455.     number" },
  456.  
  457.   { "unalias", unalias_builtin, 1, "unalias [name ...]",
  458.       "    Remove NAMEs from the list of defined aliases" },
  459.  
  460.   { "unset", unset_builtin, 1, "unset [name ...]",
  461.       "    For each NAME, remove the corresponding variable or function.  Note\n\
  462.     that PATH and IFS cannot be unset" },
  463.  
  464.   { "wait", wait_builtin, 1, "wait [n]",
  465.       "    Wait for the specified process and report its termination\n\
  466.     status.  If N is not given, all currently active child processes\n\
  467.     are waited for, and the return code is zero" },
  468.  
  469.   /* This marks the end of the functions which are builtins per-se.  The
  470.      following are actually parser constructs. */
  471.   { "for", (Function *)0x0, 1, "for NAME [in WORDS ...] ; do COMMANDS ; done",
  472.       "    The `for' loop executes a sequence of commands for each member in a\n\
  473.     list of items.  If \"in WORDS ...\" is not present, then \"in $*\" is\n\
  474.     assumed.  For each element in WORDS, NAME is set to that element, and\n\
  475.     the COMMANDS are executed" },
  476.  
  477.   { "case", (Function *)0x0, 1, "case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac",
  478.       "    Selectively execute COMMANDS based upon WORD matching PATTERN.  The\n\
  479.     `|' is used to separate multiple patterns" },
  480.  
  481.   { "if", (Function *)0x0, 1, "if COMMANDS then COMMANDS [else COMMANDS] fi",
  482.       "    `if' executes the `then' COMMANDS only if the final command in the `if'\n\
  483.     COMMANDS has an exit status of zero" },
  484.  
  485.   { "while", (Function *)0x0, 1, "while COMMANDS do COMMANDS done",
  486.       "    Expand and execute COMMANDS as long as the final command in the `while'\n\
  487.     COMMANDS has an exit status of zero" },
  488.  
  489.   { "until", (Function *)0x0, 1, "until COMMAND do COMMANDS done",
  490.       "    Expand and execute COMMANDS as long as the final command in the `until'\n\
  491.     COMMANDS has an exit status which is not zero" },
  492.  
  493.   { "function", (Function *)0x0, 1, "function NAME { COMMANDS ; } or NAME () { COMMANDS ; }",
  494.       "    Create a simple command invoked by NAME which runs COMMANDS.  Arguments\n\
  495.     on the command line along with NAME are passed to the function as\n\
  496.     $0 .. $n" },
  497.   { "{ ... }", (Function *)0x0, 1, "{ COMMANDS }",
  498.       "    Run a set of commands in a group.  This is one way to redirect an\n\
  499.     entire set of commands" },
  500.  
  501. #ifdef JOB_CONTROL
  502.   { "%", (Function *)0x0, 1, "%[DIGITS | WORD] [&]",
  503.       "    This is similar to the `fg' command.  Resume a stopped or background\n\
  504.     job.  If you specifiy DIGITS, then that job is used.  If you specify\n\
  505.     WORD, then the job whose name begins with WORD is used.  Following\n\
  506.     the job specification with a `&' places the job in the background" },
  507. #endif
  508.  
  509.   { (char *)0x0, (Function *)0x0, 0, (char *)0x0, (char *)0x0 }
  510.  
  511. };
  512.  
  513. /* Enable the shell command NAME.  If DISABLE_P is non-zero, then
  514.    disable NAME instead. */
  515. enable_shell_command (name, disable_p)
  516.      char *name;
  517.      int disable_p;
  518. {
  519.   register int i;
  520.   int found = 0;
  521.  
  522.   for (i = 0; shell_builtins[i].function; i++)
  523.     if (strcmp (name, shell_builtins[i].name) == 0)
  524.       {
  525.     found++;
  526.     shell_builtins[i].enabled = !disable_p;
  527.       }
  528.  
  529.   return (found);
  530. }
  531.  
  532. /* Enable/disable shell commands present in LIST.  If list is not specified,
  533.    then print out a list of shell commands showing which are enabled and
  534.    which are disabled. */
  535. enable_builtin (list)
  536.      WORD_LIST *list;
  537. {
  538.   int result = 0;
  539.  
  540.   if (!list)
  541.     {
  542.       register int i;
  543.  
  544.       for (i = 0; shell_builtins[i].function; i++)
  545.     printf ("enable %s%s\n", shell_builtins[i].enabled ? "" : "-n ",
  546.         shell_builtins[i].name);
  547.     }
  548.   else
  549.     {
  550.       int disable_p = (strcmp (list->word->word, "-n") == 0);
  551.  
  552.       if (disable_p)
  553.     list = list->next;
  554.  
  555.       while (list)
  556.     {
  557.       result = enable_shell_command (list->word->word, disable_p);
  558.       list = list->next;
  559.     }
  560.     }
  561.   return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  562. }
  563.  
  564. /* Print out a list of the known functions in the shell, and what they do.
  565.    If LIST is supplied, print out the list which matches for each pattern
  566.    specified. */
  567. help_builtin (list)
  568.      WORD_LIST *list;
  569. {
  570.   if (!list)
  571.     {
  572.       register int j, i = 0;
  573.       char blurb[256];
  574.  
  575.       show_shell_version ();
  576.       printf (
  577. "Shell commands that are defined internally.  Type `help' to see this list.\n\
  578. Type `help name' to find out more about the function `name'.\n\
  579. Use `info bash' to find out more about the shell in general.\n\
  580. \n\
  581. A star (*) next to a name means that the command is disabled.\n\
  582. \n");
  583.  
  584.       for (i = 0; shell_builtins[i].name; i++)
  585.     {
  586.       sprintf (blurb, "%c%s", shell_builtins[i].enabled ? ' ' : '*',
  587.            shell_builtins[i].short_doc);
  588.  
  589.       blurb[35] = '\0';
  590.       printf ("%s", blurb);
  591.  
  592.       if (i % 2)
  593.         printf ("\n");
  594.       else
  595.         for (j = strlen (blurb); j < 35; j++)
  596.           putc (' ', stdout);
  597.         
  598.     }
  599.       if (i % 2)
  600.     printf ("\n");
  601.     }
  602.   else
  603.     {
  604.       int match_found = 0;
  605.       char *pattern = "";
  606.       
  607.       if (glob_pattern_p (list->word->word))
  608.     {
  609.       printf ("Shell commands matching keyword%s `",
  610.           list->next ? "s" : "");
  611.       print_word_list (list, ", ");
  612.       printf ("'\n\n");
  613.     }
  614.       while (list)
  615.     {
  616.       register int i = 0, plen;
  617.  
  618.       pattern = list->word->word;
  619.       plen = strlen (pattern);
  620.  
  621.       while (shell_builtins[i].name)
  622.         {
  623.           if (glob_match (pattern, shell_builtins[i].name, 0) ||
  624.           strnicmp (pattern, shell_builtins[i].name, plen) == 0)
  625.         {
  626.           printf ("%s: %s\n%s.\n",
  627.               shell_builtins[i].name, shell_builtins[i].short_doc,
  628.               shell_builtins[i].long_doc);
  629.           match_found++;
  630.         }
  631.           i++;
  632.         }
  633.       list = list->next;
  634.     }
  635.       if (!match_found)
  636.     {
  637.       printf ("No help topics match `%s'.  Try `help help'.\n", pattern);
  638.       fflush (stdout);
  639.       return (EXECUTION_FAILURE);
  640.     }
  641.     }
  642.   fflush (stdout);
  643.   return (EXECUTION_SUCCESS);
  644. }
  645.  
  646. /* Do nothing.  This command is a no-op. */
  647. colon_builtin ()
  648. {
  649.   return (EXECUTION_SUCCESS);
  650. }
  651.  
  652. /* Read and execute commands from the file passed as argument.  Guess what.
  653.    This cannot be done in a subshell, since things like variable assignments
  654.    take place in there.  So, I open the file, place it into a large string,
  655.    close the file, and then execute the string. */
  656. period_builtin (list)
  657.      WORD_LIST *list;
  658. {
  659.   int result = (EXECUTION_SUCCESS);
  660.   int push_dollar_vars (), pop_dollar_vars ();
  661.     
  662.   if (list)
  663.     {
  664.       int fd;            /* File descriptor. */
  665.       int tt;            /* Temporary result. */
  666.       char *string;        /* String to execute. */
  667.       char *filename, *tempfile;
  668.       extern char *find_path_file ();
  669.       struct stat finfo;
  670.  
  671.       tempfile = find_path_file (list->word->word);
  672.  
  673.       if (!tempfile)
  674.     tempfile = savestring (list->word->word);
  675.  
  676.       filename = (char *)alloca (1 + strlen (tempfile));
  677.       strcpy (filename, tempfile);
  678.       free (tempfile);
  679.  
  680.       if (stat (filename, &finfo) == -1 ||
  681.       (fd = open (filename, O_RDONLY)) == -1)
  682.     goto file_error_exit;
  683.  
  684.       string = (char *)xmalloc  (1 + finfo.st_size);
  685.       tt = read (fd, string, finfo.st_size);
  686.  
  687.       /* Close the open file, preserving the state of errno. */
  688.       { int temp = errno; close (fd); errno = temp; }
  689.  
  690.       if (tt != finfo.st_size)
  691.     {
  692.       free (string);
  693.     file_error_exit:
  694.       file_error (filename);
  695.       return (EXECUTION_FAILURE);
  696.     }
  697.  
  698.       push_dollar_vars ();
  699.       remember_args (list->next, 1);
  700.       begin_unwind_frame ("file_sourceing");
  701.       add_unwind_protect (pop_dollar_vars, (char *)NULL);
  702.       string[finfo.st_size] = '\0';
  703.       result = parse_and_execute (string, filename);
  704.       run_unwind_frame ("file_sourceing");
  705.     }
  706.   return (result);
  707. }
  708.  
  709. /* Parse and execute the commands in STRING.  Returns whatever
  710.    execute_command () returns.  This frees STRING. */
  711. int
  712. parse_and_execute (string, from_file)
  713.      char *string;
  714.      char *from_file;
  715. {
  716.   extern int remember_on_history;
  717.   extern int history_expansion_inhibited;
  718.   extern int indirection_level;
  719.   char *indirection_level_string ();
  720.  
  721.   int old_interactive = interactive;
  722.   int old_remember_on_history = remember_on_history;
  723.   int old_history_expansion_inhibited = history_expansion_inhibited;
  724.   int last_result = EXECUTION_SUCCESS;
  725.   char *orig_string = string;
  726.   extern COMMAND *global_command;
  727.  
  728.   push_stream ();
  729.   interactive = 0;
  730.   indirection_level++;
  731.  
  732.   /* We don't remember text read by the shell this way on
  733.      the history list, and we don't use !$ in shell scripts. */
  734.   remember_on_history = 0;
  735.   history_expansion_inhibited = 1;
  736.   
  737.   with_input_from_string (string, from_file);
  738.   {
  739.     extern char *yy_input_dev;
  740.     COMMAND *command;
  741.  
  742.     while (*yy_input_dev)
  743.       {
  744.     if (interrupt_state)
  745.       {
  746.         last_result = EXECUTION_FAILURE;
  747.         break;
  748.       }
  749.  
  750.     if (yyparse () == 0)
  751.       {
  752.         if ((command = global_command) != (COMMAND *)NULL)
  753.           {
  754.         global_command = (COMMAND *)NULL;
  755.  
  756.         last_result = execute_command (command);
  757.         dispose_command (command);
  758.           }
  759.       }
  760.     else
  761.       {
  762.         last_result = EXECUTION_FAILURE;
  763.  
  764.         /* Since we are shell compatible, syntax errors in a script
  765.            abort the execution of the script.  Right? */
  766.         break;
  767.       }
  768.       }
  769.   }
  770.  
  771.   remember_on_history = old_remember_on_history;
  772.   history_expansion_inhibited = old_history_expansion_inhibited;
  773.   interactive = old_interactive;
  774.   pop_stream ();
  775.   indirection_level--;
  776.   if (orig_string)
  777.     free (orig_string);
  778.  
  779.   return (last_result);
  780. }
  781.  
  782. /* Set up to break x levels, where x defaults to 1, but can be specified
  783.    as the first argument. */
  784.  
  785. /* The depth of while's and until's. */
  786. int loop_level = 0;
  787.  
  788. /* Non-zero when a "break" instruction is encountered. */
  789. int breaking = 0;
  790.  
  791. /* Return non-zero if a break or continue command would be okay.
  792.    Print an error message if break or continue is meaningless here. */
  793. check_loop_level ()
  794. {
  795.   extern char *this_command_name;
  796.  
  797. #ifdef BREAK_COMPLAINS
  798.   if (!loop_level)
  799.     builtin_error ("Only meaningful in a `for', `while', or `until' loop");
  800. #endif
  801.   return (loop_level);
  802. }
  803.  
  804. break_builtin (list)
  805.      WORD_LIST *list;
  806. {
  807.  
  808.   if (!check_loop_level ())
  809.     return (EXECUTION_FAILURE);
  810.  
  811.   breaking = get_numeric_arg (list);
  812.  
  813.   if (breaking < 0)
  814.     breaking = 0;
  815.  
  816.   if (breaking > loop_level)
  817.     breaking = loop_level;
  818.  
  819.   return (EXECUTION_SUCCESS);
  820. }
  821.  
  822. /* Set up to continue x levels, where x defaults to 1, but can be specified
  823.    as the first argument. */
  824.  
  825. /* Non-zero when we have encountered a continue instruction. */
  826. int continuing = 0;
  827.  
  828. continue_builtin (list)
  829.      WORD_LIST *list;
  830. {
  831.  
  832.   if (!check_loop_level ())
  833.     return (EXECUTION_FAILURE);
  834.  
  835.   continuing = get_numeric_arg (list);
  836.  
  837.   if (continuing < 0)
  838.     continuing = 0;
  839.  
  840.   if (continuing > loop_level)
  841.     continuing = loop_level;
  842.   
  843.   return (EXECUTION_SUCCESS);
  844. }
  845.  
  846. /* Read a numeric arg for this_command_name, the name of the shell builtin
  847.    that wants it.  LIST is the word list that the arg is to come from. */
  848. get_numeric_arg (list)
  849.      WORD_LIST *list;
  850. {
  851.   int count = 1;
  852.  
  853.   if (list)
  854.     {
  855.       if (sscanf (list->word->word, "%d", &count) != 1)
  856.     {
  857.       builtin_error ("bad non-numeric arg `%s'", list->word->word);
  858.       longjmp (top_level, DISCARD);
  859.     }
  860.       no_args (list->next);
  861.     }
  862.   return (count);
  863. }
  864.  
  865. /* Change the current working directory to the first word in LIST, or
  866.    to $HOME if there is no LIST.  Do nothing in the case that there is
  867.    no $HOME nor LIST. If the variable CDPATH exists, use that as the search
  868.    path for finding the directory.  If all that fails, and the variable
  869.    `cdable_vars' exists, then try the word as a variable name.  If that
  870.    variable has a value, then cd to the value of that variable. */
  871.  
  872. /* By default, follow the symbolic links as if they were real directories
  873.    while hacking the `cd' command.  This means that `cd ..' moves up in
  874.    the string of symbolic links that make up the current directory, instead
  875.    of the absolute directory.  The shell variable `nolinks' controls this
  876.    flag. */
  877. int follow_symbolic_links = 1;
  878.  
  879. /* In order to keep track of the working directory, we have this static
  880.    variable hanging around. */
  881. static char *the_current_working_directory = (char *)NULL;
  882.  
  883. cd_builtin (list)
  884.      WORD_LIST *list;
  885. {
  886.   char *dirname;
  887.  
  888. #ifdef FACIST
  889.   {
  890.     extern int restricted;
  891.     if (restricted)
  892.       {
  893.     builtin_error ("Privileged command");
  894.     return (EXECUTION_FAILURE);
  895.       }
  896.   }
  897. #endif
  898.  
  899.   if (list)
  900.     {
  901.       char *extract_colon_unit ();
  902.       char *path_string = get_string_value ("CDPATH");
  903.       char *path;
  904.       int index = 0;
  905.  
  906.       dirname = list->word->word;
  907.  
  908.       if (path_string && !absolute_pathname (dirname))
  909.     {
  910.       while ((path = extract_colon_unit (path_string, &index)))
  911.         {
  912.           char *dir;
  913.  
  914.           if (!disallow_filename_globbing && *path)
  915.         {
  916.           char *tilde_expand (), *te_string = tilde_expand (path);
  917.  
  918.           free (path);
  919.           path = te_string;
  920.         }
  921.  
  922.           dir = (char *)alloca (2 + strlen (dirname) + strlen (path));
  923.  
  924.           if (!dir)
  925.         abort ();
  926.  
  927.           if (!*path)
  928.         {
  929.           free (path);
  930.           path = savestring ("."); /* by definition. */
  931.         }
  932.  
  933.           strcpy (dir, path);
  934.           if (path[strlen (path) - 1] != '/')
  935.         strcat (dir, "/");
  936.           strcat (dir, dirname);
  937.           free (path);
  938.  
  939.           if (change_to_directory (dir))
  940.         {
  941.           if (strncmp (dir, "./", 2) != 0)
  942.             printf ("%s\n", dir);
  943.           dirname = dir;
  944.  
  945.           goto bind_and_exit;
  946.         }
  947.         }
  948.     }
  949.  
  950.       if (!change_to_directory (dirname))
  951.     {
  952.       /* Maybe this is `cd -', equivalent to `cd $OLDPWD' */
  953.       if (dirname[0] == '-' && dirname[1] == '\0')
  954.         {
  955.           char *t = get_string_value ("OLDPWD");
  956.  
  957.           if (t && change_to_directory (t))
  958.         goto bind_and_exit;
  959.         }
  960.  
  961.       /* If the user requests it, then perhaps this is the name of
  962.          a shell variable, whose value contains the directory to
  963.          change to.  If that is the case, then change to that
  964.          directory. */
  965.       if (find_variable ("cdable_vars"))
  966.         {
  967.           char *t = get_string_value (dirname);
  968.  
  969.           if (t && change_to_directory (t))
  970.         {
  971.           printf ("%s\n", t);
  972.           goto bind_and_exit;
  973.         }
  974.         }
  975.  
  976.       file_error (dirname);
  977.       return (EXECUTION_FAILURE);
  978.     }
  979.       goto bind_and_exit;
  980.     }
  981.   else
  982.     {
  983.       dirname = get_string_value ("HOME");
  984.  
  985.       if (!dirname)
  986.     return (EXECUTION_FAILURE);
  987.  
  988.       if (!change_to_directory (dirname))
  989.     {
  990.       file_error (dirname);
  991.       return (EXECUTION_FAILURE);
  992.     }
  993.  
  994.     bind_and_exit:
  995.       {
  996.     char *get_working_directory (), *get_string_value ();
  997.     char *directory = get_working_directory ("cd");
  998.  
  999.     bind_variable ("OLDPWD", get_string_value ("PWD"));
  1000.     bind_variable ("PWD", directory);
  1001.  
  1002.     if (directory)
  1003.       free (directory);
  1004.       }
  1005.       return (EXECUTION_SUCCESS);
  1006.     }
  1007. }
  1008.  
  1009. /* Do the work of changing to the directory NEWDIR.  Handle symbolic
  1010.    link following, etc. */
  1011. change_to_directory (newdir)
  1012.      char *newdir;
  1013. {
  1014.   char *get_working_directory (), *make_absolute ();
  1015.   char *t;
  1016.  
  1017.   if (follow_symbolic_links)
  1018.     {
  1019.       if (!the_current_working_directory)
  1020.     {
  1021.       t = get_working_directory ("cd_links");
  1022.       if (t)
  1023.         free (t);
  1024.     }
  1025.  
  1026.       if (the_current_working_directory)
  1027.     t = make_absolute (newdir, the_current_working_directory);
  1028.       else
  1029.     t = savestring (newdir);
  1030.  
  1031.       /* Get rid of trailing `/'. */
  1032.       {
  1033.     register int len_t = strlen (t);
  1034.     if (len_t > 1)
  1035.       {
  1036.         --len_t;
  1037.         if (t[len_t] == '/')
  1038.           t[len_t] = '\0';
  1039.       }
  1040.       }
  1041.  
  1042.       if (chdir (t) < 0)
  1043.     {
  1044.       free (t);
  1045.       return (0);
  1046.     }
  1047.  
  1048.       if (the_current_working_directory)
  1049.     strcpy (the_current_working_directory, t);
  1050.  
  1051.       free (t);
  1052.       return (1);
  1053.     }
  1054.   else
  1055.     {
  1056.       if (chdir (newdir) < 0)
  1057.     return (0);
  1058.       else
  1059.     return (1);
  1060.     }
  1061. }
  1062.  
  1063. /* Return a consed string which is the current working directory.
  1064.    FOR_WHOM is the name of the caller for error printing.  */
  1065. char *
  1066. get_working_directory (for_whom)
  1067.      char *for_whom;
  1068. {
  1069.   if (!follow_symbolic_links)
  1070.     {
  1071.       if (the_current_working_directory)
  1072.     free (the_current_working_directory);
  1073.  
  1074.       the_current_working_directory = (char *)NULL;
  1075.     }
  1076.  
  1077.   if (!the_current_working_directory)
  1078.     {
  1079.       char *directory, *getwd ();
  1080.  
  1081.       the_current_working_directory = (char *)xmalloc (MAXPATHLEN);
  1082.       directory = getwd (the_current_working_directory);
  1083.       if (!directory)
  1084.     {
  1085.       fprintf (stderr, "%s: %s\n",
  1086.            for_whom, the_current_working_directory);
  1087.       free (the_current_working_directory);
  1088.       the_current_working_directory = (char *)NULL;
  1089.       return (char *)NULL;
  1090.     }
  1091.     }
  1092.  
  1093.   return (savestring (the_current_working_directory));
  1094. }
  1095.  
  1096.  
  1097. /* Print the words in LIST to standard output.  If the first word is
  1098.    `-n', then don't print a trailing newline.  We also support the
  1099.    echo syntax from Version 9 unix systems. */
  1100. echo_builtin (list)
  1101.      WORD_LIST *list;
  1102. {
  1103.   int display_return = 1, do_v9 = 0;
  1104.  
  1105. /* System V machines already have a /bin/sh with a v9 behaviour.  We
  1106.    give Bash the identical behaviour for these machines so that the
  1107.    existing system shells won't barf. */
  1108. #if defined (V9_ECHO) && defined (SYSV)
  1109.          do_v9 = 1;
  1110. #endif
  1111.  
  1112.   while (list && list->word->word[0] == '-')
  1113.     {
  1114.       char *temp = &(list->word->word[1]);
  1115.  
  1116.       if (!*temp)
  1117.     goto just_echo;
  1118.  
  1119.       while (*temp)
  1120.     {
  1121.       if (*temp == 'n')
  1122.         display_return = 0;
  1123. #ifdef V9_ECHO
  1124.       else if (*temp == 'e')
  1125.         do_v9 = 1;
  1126. #ifdef SYSV
  1127.       else if (*temp == 'E')
  1128.         do_v9 = 0;
  1129. #endif
  1130. #endif
  1131.       else
  1132.         goto just_echo;
  1133.       
  1134.       temp++;
  1135.     }
  1136.       list = list->next;
  1137.     }
  1138.  
  1139. just_echo:
  1140.  
  1141.   if (list)
  1142.     {
  1143. #ifdef V9_ECHO
  1144.       if (do_v9)
  1145.     {
  1146.       while (list)
  1147.         {
  1148.           register char *s = list->word->word;
  1149.           register int c;
  1150.  
  1151.           while (c = *s++)
  1152.         {
  1153.           if (c == '\\' && *s)
  1154.             {
  1155.               switch (c = *s++)
  1156.             {
  1157.             case 'b': c = '\b'; break;
  1158.             case 'c': display_return = 0; continue;
  1159.             case 'f': c = '\f'; break;
  1160.             case 'n': c = '\n'; break;
  1161.             case 'r': c = '\r'; break;
  1162.             case 't': c = '\t'; break;
  1163.             case 'v': c = (int) 0x0B; break;
  1164.             case '0': case '1': case '2': case '3':
  1165.             case '4': case '5': case '6': case '7':
  1166.               c -= '0';
  1167.               if (*s >= '0' && *s <= '7')
  1168.                 c = c * 8 + (*s++ - '0');
  1169.               if (*s >= '0' && *s <= '7')
  1170.                 c = c * 8 + (*s++ - '0');
  1171.               break;
  1172.             case '\\': break;
  1173.             default:  putchar ('\\'); break;
  1174.             }
  1175.             }
  1176.           putchar(c);
  1177.         }
  1178.           list = list->next;
  1179.           if (list)
  1180.         putchar(' ');
  1181.         }
  1182.     }
  1183.       else
  1184. #endif  /* V9_ECHO */    
  1185.     print_word_list (list, " ");
  1186.     }
  1187.   if (display_return)
  1188.     printf ("\n");
  1189.   fflush (stdout);
  1190.   return (EXECUTION_SUCCESS);
  1191. }
  1192.  
  1193. /* Parse the string that these words make, and execute the command found. */
  1194. eval_builtin (list)
  1195.      WORD_LIST *list;
  1196. {
  1197.   char *string_list ();
  1198.   int result;
  1199.  
  1200.   /* Note that parse_and_execute () free ()'s what it is passed. */
  1201.   if (list)
  1202.     result = parse_and_execute (string_list (list), "eval");
  1203.   else
  1204.     result = EXECUTION_SUCCESS;
  1205.   return (result);
  1206. }
  1207.  
  1208. /* Defined in execute_cmd.c */
  1209. extern REDIRECT *redirection_undo_list;
  1210.  
  1211. exec_builtin (list)
  1212.      WORD_LIST *list;
  1213. {
  1214.   extern char *find_user_command ();
  1215.  
  1216.   maybe_make_export_env ();
  1217.  
  1218.   /* First, let the redirections remain. */
  1219.   dispose_redirects (redirection_undo_list);
  1220.   redirection_undo_list = (REDIRECT *)NULL;
  1221.  
  1222.   if (!list)
  1223.     return (EXECUTION_SUCCESS);
  1224.   else
  1225.     {
  1226.       /* Otherwise, execve the new command with args. */
  1227.       char *command, **args;
  1228.       int dash_name = 0;
  1229.  
  1230.       if (strcmp (list->word->word, "-") == 0)
  1231.     {
  1232.       /* The user would like to exec this command as if it was a
  1233.          login command.  Do so. */
  1234.       list = list->next;
  1235.       dash_name++;
  1236.     }
  1237.  
  1238.       args = (char **)make_word_array (list);
  1239.       command = find_user_command (args[0]);
  1240.       if (!command)
  1241.     {
  1242.       builtin_error ("%s: not found", args[0]);
  1243.       goto failed_exec;
  1244.     }
  1245.  
  1246.       command = (char *)full_pathname (command);
  1247.       /* If the user wants this to look like a login shell, then
  1248.      pass the full pathname in argv[0]. */
  1249.       if (dash_name)
  1250.     {
  1251.       char *new_name = (char *)xmalloc (1 + strlen (command));
  1252.       strcpy (new_name, "-");
  1253.       strcat (new_name, command);
  1254.       args[0] = new_name;
  1255.     }
  1256.  
  1257.       write_history (get_string_value ("HISTFILE"));
  1258.  
  1259.       signal (SIGINT, SIG_DFL);
  1260.       signal (SIGQUIT, SIG_DFL);
  1261.       execve (command, args, export_env);
  1262.  
  1263.       if (!executable_file (command))
  1264.     builtin_error ("%s: cannot execute", command);
  1265.       else
  1266.     file_error (command);
  1267.  
  1268.     failed_exec:
  1269.       if (!interactive && !find_variable ("no_exit_on_failed_exec"))
  1270.     exit (EXECUTION_FAILURE);
  1271.       return (EXECUTION_FAILURE);
  1272.     }
  1273. }
  1274.  
  1275. exit_builtin (list)
  1276.      WORD_LIST *list;
  1277. {
  1278.   extern int login_shell;
  1279.  
  1280.   if (interactive && login_shell)
  1281.     {
  1282.       fprintf (stderr, "logout\n");
  1283.       fflush (stderr);
  1284.     }
  1285.  
  1286.   exit_or_logout (list);
  1287. }
  1288.  
  1289. /* How to logout. */
  1290. logout_builtin (list)
  1291.      WORD_LIST *list;
  1292. {
  1293.   if (!login_shell && interactive)
  1294.     {
  1295.       builtin_error ("Not login shell: use `exit' or `bye'");
  1296.       return (EXECUTION_FAILURE);
  1297.     }
  1298.   else
  1299.     exit_or_logout (list);
  1300. }
  1301.  
  1302. /* Clean up work for exiting or logging out. */
  1303. Function *last_shell_builtin = (Function *)NULL;
  1304. Function *this_shell_builtin = (Function *)NULL;
  1305.  
  1306. exit_or_logout (list)
  1307.      WORD_LIST *list;
  1308. {
  1309.   extern int last_command_exit_value;
  1310.  
  1311. #ifdef JOB_CONTROL
  1312.   int exit_immediate_okay;
  1313.  
  1314.   exit_immediate_okay = (!interactive ||
  1315.              last_shell_builtin == exit_builtin ||
  1316.              last_shell_builtin == logout_builtin ||
  1317.              last_shell_builtin == jobs_builtin);
  1318.  
  1319.   /* Check for stopped jobs if the user wants to. */
  1320.   if (1 && !exit_immediate_okay)
  1321.     {
  1322.       register int i;
  1323.       for (i = 0; i < job_slots; i++)
  1324.     if (jobs[i] && (jobs[i]->state == JSTOPPED))
  1325.       {
  1326.         fprintf (stderr, "There are stopped jobs.\n");
  1327.  
  1328.         /* This is NOT superfluous because EOF can get here without
  1329.            going through the command parser. */
  1330.         last_shell_builtin = exit_builtin;
  1331.  
  1332.         longjmp (top_level, DISCARD);
  1333.       }
  1334.     }
  1335. #endif
  1336.  
  1337.   /* Get return value if present.  This means that you can type
  1338.      `logout 5' to a shell, and it returns 5. */
  1339.  
  1340.   if (list)
  1341.     last_command_exit_value = get_numeric_arg (list);
  1342.  
  1343.   /* Run our `~/.bash_logout' file if it exists, and this is a login shell. */
  1344.   if (login_shell)
  1345.     maybe_execute_file ("~/.bash_logout");
  1346.  
  1347.   /* Exit the program. */
  1348.   longjmp (top_level, EXITPROG);
  1349. }
  1350.  
  1351. /* For each variable name in LIST, make that variable appear in the
  1352.    environment passed to simple commands.  If there is no LIST, then
  1353.    print all such variables.  An argument of `-n' says to remove the
  1354.    exported attribute from variables named in LIST. */
  1355. export_builtin (list)
  1356.      register WORD_LIST *list;
  1357. {
  1358.   return (set_or_show_attributes (list, att_exported));
  1359. }
  1360.  
  1361. /* For each variable name in LIST, make that variable have the specified
  1362.    ATTRIBUTE.  An arg of `-n' says to remove the attribute from the the
  1363.    remaining names in LIST.  */
  1364. set_or_show_attributes (list, attribute)
  1365.      register WORD_LIST *list;
  1366.      int attribute;
  1367. {
  1368.   register SHELL_VAR *var;
  1369.   int assign, undo = 0;
  1370.   extern int array_needs_making;
  1371.  
  1372.   if (list)
  1373.     {
  1374.       while (list)
  1375.     {
  1376.       register char *name = list->word->word;
  1377.  
  1378.       if (strcmp (name, "-n") == 0)
  1379.         {
  1380.           undo++;
  1381.           list = list->next;
  1382.           continue;
  1383.         }
  1384.  
  1385.       var = find_variable (name);
  1386.  
  1387.       if ((assign = assignment (name)) != 0)
  1388.         {
  1389.           do_assignment (name);
  1390.           name[assign] = '\0';
  1391.         }
  1392.  
  1393.       if (undo)
  1394.         {
  1395.           var = find_variable (name);
  1396.           if (var)
  1397.         var->attributes &= ~attribute;
  1398.         }
  1399.       else
  1400.         {
  1401.           var = find_variable (name);
  1402.  
  1403.           if (!var)
  1404.         {
  1405.           SHELL_VAR *find_tempenv_variable ();
  1406.  
  1407.           if ((var = find_tempenv_variable (name)))
  1408.             {
  1409.               SHELL_VAR *disposer = var;
  1410.               var = bind_variable (disposer->name, disposer->value);
  1411.               dispose_variable (disposer);
  1412.             }
  1413.           else
  1414.             {
  1415.               var = bind_variable (name, (char *)NULL);
  1416.               var->attributes |= att_invisible;
  1417.             }
  1418.         }
  1419.  
  1420.           var->attributes |= attribute;
  1421.         }
  1422.       array_needs_making++;
  1423.  
  1424.       list = list->next;
  1425.     }
  1426.     }
  1427.   else
  1428.     {
  1429.       var = variable_list;
  1430.  
  1431.       while (var)
  1432.     {
  1433.       if ((var->attributes & attribute) && !invisible_p (var))
  1434.         {
  1435.           char flags[5];
  1436.  
  1437.           flags[0] = '\0';
  1438.  
  1439.           if (var->attributes & att_exported)
  1440.         strcat (flags, "x");
  1441.  
  1442.           if (var->attributes & att_readonly)
  1443.         strcat (flags, "r");
  1444.  
  1445.           if (function_p (var))
  1446.          strcat (flags, "f");
  1447.  
  1448.           if (flags[0])
  1449.         {
  1450.           printf ("declare -%s ", flags);
  1451.  
  1452.           if (!function_p (var))
  1453.             printf ("%s=%s\n", var->name, value_cell (var));
  1454.           else
  1455.             {
  1456.               char *named_function_string ();
  1457.  
  1458.               printf ("%s\n",
  1459.                   named_function_string (var->name,
  1460.                              function_cell (var), 1));
  1461.             }
  1462.         }
  1463.         }
  1464.       var = var->next;
  1465.     }
  1466.     }
  1467.   return (EXECUTION_SUCCESS);
  1468. }
  1469.  
  1470. /* Hashing filenames in the shell. */
  1471.  
  1472. #include "hash.h"
  1473.  
  1474. #define FILENAME_HASH_BUCKETS 631
  1475.  
  1476. HASH_TABLE *hashed_filenames;
  1477.  
  1478. typedef struct {
  1479.   /* The full pathname of the file. */
  1480.   char *path;
  1481.  
  1482.   /* Whether `.' appeared before this one in $PATHS. */
  1483.   int check_dot;
  1484. } PATH_DATA;
  1485.  
  1486. #define pathdata(x) ((PATH_DATA *)(x)->data)
  1487.  
  1488. initialize_filename_hashing ()
  1489. {
  1490.   hashed_filenames = make_hash_table (FILENAME_HASH_BUCKETS);
  1491. }
  1492.  
  1493. /* Place FILENAME (key) and FULL_PATHNAME (data->path) into the
  1494.    hash table.  CHECK_DOT if non-null is for future calls to
  1495.    find_hashed_filename (). */
  1496. remember_filename (filename, full_pathname, check_dot)
  1497.      char *filename, *full_pathname;
  1498.      int check_dot;
  1499. {
  1500.   register BUCKET_CONTENTS *item;
  1501.  
  1502.   if (hashing_disabled)
  1503.     return;
  1504.   item = add_hash_item (filename, hashed_filenames);
  1505.   if (item->data)
  1506.     free (pathdata(item)->path);
  1507.   else
  1508.     item->data = (char *)xmalloc (sizeof (PATH_DATA));
  1509.  
  1510.   item->key = savestring (filename);
  1511.   pathdata(item)->path = savestring (full_pathname);
  1512.   pathdata(item)->check_dot = check_dot;
  1513.   item->times_found = 0;
  1514. }
  1515.  
  1516. /* Temporary static. */
  1517. char *dotted_filename = (char *)NULL;
  1518.  
  1519. /* Return the full pathname that FILENAME hashes to.  If FILENAME
  1520.    is hashed, but data->check_dot is non-zero, check ./FILENAME
  1521.    and return that if it is executable. */
  1522. char *
  1523. find_hashed_filename (filename)
  1524.      char *filename;
  1525. {
  1526.   register BUCKET_CONTENTS *item;
  1527.  
  1528.   if (hashing_disabled)
  1529.     return ((char *)NULL);
  1530.  
  1531.   item = find_hash_item (filename, hashed_filenames);
  1532.   if (item) {
  1533.  
  1534.     /* If this filename is hashed, but `.' comes before it in the path,
  1535.        then see if `./filename' is an executable. */
  1536.     if (pathdata(item)->check_dot) {
  1537.  
  1538.       if (dotted_filename)
  1539.     free (dotted_filename);
  1540.  
  1541.       dotted_filename = (char *)xmalloc (3 + strlen (filename));
  1542.       strcpy (dotted_filename, "./");
  1543.       strcat (dotted_filename, filename);
  1544.  
  1545.       if (executable_file (dotted_filename))
  1546.     return (dotted_filename);
  1547.  
  1548.       /* Watch out.  If this file was hashed to "./filename", and
  1549.      "./filename" is not executable, then return NULL. */
  1550.       if (strcmp (pathdata(item)->path, dotted_filename) == 0)
  1551.     return ((char *)NULL);
  1552.     }
  1553.     return (pathdata(item)->path);
  1554.   } else {
  1555.     return ((char *)NULL);
  1556.   }
  1557. }
  1558.  
  1559. /* Print statistics on the current state of hashed commands.  If LIST is
  1560.    not empty, then rehash (or hash in the first place) the specified
  1561.    commands. */
  1562. hash_builtin (list)
  1563.      WORD_LIST *list;
  1564. {
  1565.   extern Function *find_shell_builtin ();
  1566.   extern char *find_user_command ();
  1567.   int any_failed = 0;
  1568.  
  1569.   if (hashing_disabled)
  1570.     {
  1571.       builtin_error ("Hashing is disabled");
  1572.       return (EXECUTION_FAILURE);
  1573.     }
  1574.  
  1575.   if (!list)
  1576.     {
  1577.       /* Print information about current hashed info. */
  1578.       int any_printed = 0;
  1579.       int bucket = 0;
  1580.       register BUCKET_CONTENTS *item_list;
  1581.  
  1582.       while (bucket < hashed_filenames->nbuckets)
  1583.     {
  1584.       item_list = get_hash_bucket (bucket, hashed_filenames);
  1585.       if (item_list)
  1586.         {
  1587.           if (!any_printed)
  1588.         {
  1589.           printf ("hits\tcommand\n");
  1590.           any_printed++;
  1591.         }
  1592.           while (item_list)
  1593.         {
  1594.           printf ("%4d\t%s\n",
  1595.               item_list->times_found, pathdata(item_list)->path);
  1596.           item_list = item_list->next;
  1597.         }
  1598.         }
  1599.       bucket++;
  1600.     }
  1601.       if (!any_printed)
  1602.     {
  1603.       printf ("No commands in hash table.\n");
  1604.     }
  1605.     }
  1606.   else
  1607.     {
  1608.       /* Add or rehash the specified commands. */
  1609.       char *word;
  1610.       char *full_path;
  1611.       SHELL_VAR *var;
  1612.       int any_failed = 0;
  1613.  
  1614.       if (strcmp (list->word->word, "-r") == 0)
  1615.     {
  1616.       int bucket = 0;
  1617.       register BUCKET_CONTENTS *item_list, *prev;
  1618.  
  1619.       while (bucket < hashed_filenames->nbuckets)
  1620.         {
  1621.           item_list = get_hash_bucket (bucket, hashed_filenames);
  1622.           if (item_list)
  1623.         {
  1624.           while (item_list)
  1625.             {
  1626.               prev = item_list;
  1627.               free (item_list->key);
  1628.               free (pathdata(item_list)->path);
  1629.               free (item_list->data);
  1630.               item_list = item_list->next;
  1631.               free (prev);
  1632.             }
  1633.           hashed_filenames->bucket_array[bucket] = (BUCKET_CONTENTS *)NULL;
  1634.         }
  1635.           bucket++;
  1636.         }
  1637.       list = list->next;
  1638.     }
  1639.       while (list)
  1640.     {
  1641.       word = list->word->word;
  1642.       if (absolute_pathname (word))
  1643.         goto next;
  1644.       full_path = find_user_command (word);
  1645.       var = find_variable (word);
  1646.  
  1647.       if (!find_shell_builtin (word) &&
  1648.           (!var || !function_p (var)))
  1649.         {
  1650.           if (full_path && executable_file (full_path))
  1651.         {
  1652.           extern int dot_found_in_search;
  1653.           remember_filename (word, full_path, dot_found_in_search);
  1654.         }
  1655.           else
  1656.         {
  1657.           builtin_error ("%s: not found", word);
  1658.           any_failed++;
  1659.         }
  1660.       }
  1661.     next:
  1662.       list = list->next;
  1663.     }
  1664.     }
  1665.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  1666. }
  1667.  
  1668. /* History.  Arg of -w FILENAME means write file, arg of -r FILENAME
  1669.    means read file.  Arg of N means only display that many items. */
  1670.  
  1671. history_builtin (list)
  1672.      WORD_LIST *list;
  1673. {
  1674.   HIST_ENTRY **hlist = history_list ();
  1675.   int limited = (list != (WORD_LIST *)NULL);
  1676.   int limit;
  1677.   register int i = 0;
  1678.  
  1679.   if (hlist)
  1680.     while (hlist[i]) i++;
  1681.  
  1682.   if (list)
  1683.     {
  1684.       if ((strcmp (list->word->word, "-w") == 0) ||
  1685.       (strcmp (list->word->word, "-r") == 0)) {
  1686.     int writing = (strcmp (list->word->word, "-w") == 0);
  1687.     char *file;
  1688.     int result;
  1689.  
  1690.     if (list->next)
  1691.       file = list->next->word->word;
  1692.     else
  1693.       file = get_string_value ("HISTFILE");
  1694.  
  1695.     if (writing)
  1696.       result = write_history (file);
  1697.     else
  1698.       result = read_history (file);
  1699.  
  1700.     return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  1701.       }
  1702.     }
  1703.  
  1704.   if (strcmp (list->word->word, "-s") == 0)
  1705.     {
  1706.       extern int history_expand ();
  1707.       char *expanded;
  1708.       int rval;
  1709.  
  1710.       list = list->next;
  1711.  
  1712.       while (list)
  1713.     {
  1714.       rval = history_expand (list->word->word, &expanded);
  1715.       printf ("%s", expanded);
  1716.       fflush (stdout);
  1717.  
  1718.       if (rval == -1)
  1719.         return (EXECUTION_FAILURE);
  1720.  
  1721.       free (expanded);
  1722.  
  1723.       list = list->next;
  1724.     }
  1725.     }
  1726.   
  1727.   limit = get_numeric_arg (list);
  1728.   if (limit < 0)
  1729.     limit = -limit;
  1730.   
  1731.   if (!limited)
  1732.     i = 0;
  1733.   else
  1734.     if ((i -= limit) < 0)
  1735.       i = 0;
  1736.  
  1737.   while (hlist[i])
  1738.     {
  1739.       fprintf (stdout,
  1740.            "%5d%c %s\n", i + history_base, hlist[i]->data ? '*' : ' ',
  1741.            hlist[i]->line);
  1742.       i++;
  1743.     }
  1744.   return (EXECUTION_SUCCESS);
  1745. }
  1746.  
  1747. /* Non-zero means that pwd always give verbatim directory, regardless of
  1748.    symbolic link following. */
  1749. int verbatim_pwd = 1;
  1750.  
  1751. /* Print the name of the current working directory. */
  1752. pwd_builtin (list)
  1753.      WORD_LIST *list;
  1754. {
  1755.   char *get_working_directory (), *getwd (), *directory;
  1756.  
  1757.   no_args (list);
  1758.  
  1759.   if (verbatim_pwd)
  1760.     {
  1761.       char *buffer = (char *)xmalloc (MAXPATHLEN);
  1762.       directory = getwd (buffer);
  1763.  
  1764.       if (!directory)
  1765.     {
  1766.       builtin_error ("%s", buffer);
  1767.       free (buffer);
  1768.     }
  1769.     }
  1770.   else
  1771.     {
  1772.       directory = get_working_directory ("pwd");
  1773.     }
  1774.  
  1775.   if (directory)
  1776.     {
  1777.       printf ("%s\n", directory);
  1778.       fflush (stdout);
  1779.       free (directory);
  1780.       return (EXECUTION_SUCCESS);
  1781.     }
  1782.   else
  1783.     {
  1784.       return (EXECUTION_FAILURE);
  1785.     }
  1786. }
  1787.  
  1788. /* Read the value of the shell variables whose names follow.
  1789.    The reading is done from the current input stream, whatever
  1790.    that may be.  Successive words of the input line are assigned
  1791.    to the variables mentioned in LIST.  The last variable in LIST
  1792.    gets the remainder of the words on the line.  If no variables
  1793.    are mentioned in LIST, then the default variable is $REPLY.
  1794.  
  1795.    S. R. Bourne's shell complains if you don't name a variable
  1796.    to receive the stuff that is read.  GNU's shell doesn't.  This
  1797.    allows you to let the user type random things. */
  1798. read_builtin (list)
  1799.      WORD_LIST *list;
  1800. {
  1801.   extern int interrupt_immediately, free ();
  1802.   register char *varname;
  1803.   int size, c, i = 0, fildes;
  1804.   char *input_string, *ifs_chars;
  1805.   WORD_LIST *words, *rwords, *list_string ();
  1806.   FILE *input_stream;
  1807.  
  1808.   ifs_chars = get_string_value ("IFS");
  1809.   input_string = (char *)xmalloc (size = 128);
  1810.  
  1811.   /* We need unbuffered input from stdin.  So we make a new
  1812.      unbuffered stream with the same file descriptor, then
  1813.      unbuffer that one. */
  1814.   fildes = dup (fileno (stdin));
  1815.  
  1816.   if (fildes == -1)
  1817.     return (EXECUTION_FAILURE);
  1818.  
  1819.   input_stream = fdopen (fildes, "r");
  1820.  
  1821.   if (!input_stream)
  1822.     {
  1823.       close (fildes);
  1824.       return (EXECUTION_FAILURE);
  1825.     }
  1826.  
  1827.   setbuf (input_stream, (char *)NULL);
  1828.  
  1829.   {
  1830.     int stream_close ();
  1831.     begin_unwind_frame ("read_builtin");
  1832.     add_unwind_protect (free, input_string);
  1833.     add_unwind_protect (stream_close, input_stream);
  1834.     interrupt_immediately++;
  1835.   }
  1836.  
  1837.   while ((c = getc (input_stream)) != EOF)
  1838.     {
  1839.       if (i + 1 >= size)
  1840.     input_string = (char *)xrealloc (input_string, size += 128);
  1841.  
  1842.       input_string[i++] = c;
  1843.  
  1844.       if (c == '\n')
  1845.     {
  1846.       if ((i >= 2) && (input_string[i - 2] == '\\'))
  1847.         {
  1848.           i -= 2;
  1849.           continue;
  1850.         }
  1851.       break;
  1852.     }
  1853.     }
  1854.   input_string[i] = '\0';
  1855.  
  1856.   interrupt_immediately--;
  1857.   discard_unwind_frame ("read_builtin");
  1858.  
  1859.   fclose (input_stream);
  1860.  
  1861.   if (!i)
  1862.     return (EXECUTION_FAILURE);    
  1863.  
  1864.   if (!list)
  1865.     {
  1866.       bind_variable ("REPLY", input_string);
  1867.       free (input_string);
  1868.     }
  1869.   else
  1870.     {
  1871.       words = list_string (input_string, ifs_chars, 0);
  1872.       rwords = words;
  1873.  
  1874.       free (input_string);
  1875.  
  1876.       while (list)
  1877.     {
  1878.       varname = list->word->word;
  1879.  
  1880.       if (!list->next)
  1881.         bind_variable (varname, words ? (char *)string_list (words) : "");
  1882.       else
  1883.         bind_variable (varname, words ? words->word->word : "");
  1884.       stupidly_hack_special_variables (varname);
  1885.       list = list->next;
  1886.       if (words)
  1887.         words = words->next;
  1888.     }
  1889.       if (rwords)
  1890.     dispose_words (rwords);
  1891.     }
  1892.  
  1893.   return (EXECUTION_SUCCESS);
  1894. }
  1895.  
  1896. /* This way I don't have to know whether fclose is a function or a macro. */
  1897. int
  1898. stream_close (file)
  1899.      FILE *file;
  1900. {
  1901.   return (fclose (file));
  1902. }
  1903.  
  1904. /* For each variable name in LIST, make that variable read_only. */
  1905. readonly_builtin (list)
  1906.      register WORD_LIST *list;
  1907. {
  1908.   return (set_or_show_attributes (list, att_readonly));
  1909. }
  1910.  
  1911. /* If we are executing a user-defined function then exit with the value
  1912.    specified as an argument.  if no argument is given, then the last
  1913.    exit status is used. */
  1914. return_builtin (list)
  1915.      WORD_LIST *list;
  1916. {
  1917.   extern int last_command_exit_value;
  1918.   extern int return_catch_flag, return_catch_value;
  1919.   extern jmp_buf return_catch;
  1920.  
  1921.   return_catch_value = get_numeric_arg (list);
  1922.  
  1923.   if (!list)
  1924.     return_catch_value = last_command_exit_value;
  1925.  
  1926.   if (return_catch_flag)
  1927.     longjmp (return_catch, 1);
  1928.   else
  1929.     {
  1930.       builtin_error ("Can only `return' from a function");
  1931.       longjmp (top_level, DISCARD);
  1932.     }
  1933. }
  1934.  
  1935. /* Set some flags from the word values in the input list.  If LIST is empty,
  1936.    then print out the values of the variables instead.  If LIST contains
  1937.    non-flags, then set $1 - $9 to the successive words of LIST. */
  1938. set_builtin (list)
  1939.      WORD_LIST *list;
  1940. {
  1941.   int on_or_off, flag_name;
  1942.  
  1943.   if (!list)
  1944.     {
  1945.       print_var_list (variable_list);
  1946.       return (EXECUTION_SUCCESS);
  1947.     }
  1948.  
  1949.   /* Do the set command.  While the list consists of words starting with
  1950.      '-' or '+' treat them as flags, otherwise, start assigning them to
  1951.      $1 ... $n. */
  1952.   while (list)
  1953.     {
  1954.       char *string = list->word->word;
  1955. #if defined (NEVER)
  1956.       if (strcmp (string, "-") == 0)
  1957.     {
  1958.       WORD_LIST *t =
  1959.         (WORD_LIST *)make_word_list (make_word ("-"), NULL);
  1960.       remember_args (t, 1);
  1961.       dispose_words (t);
  1962.       return (EXECUTION_SUCCESS);
  1963.     }
  1964. #endif /* NEVER */
  1965.  
  1966.       /* If the argument is `--' then signal the end of the list and
  1967.      remember the remaining arguments. */
  1968.       if (strcmp (string, "--") == 0)
  1969.     {
  1970.       list = list->next;
  1971.       break;
  1972.     }
  1973.  
  1974.       if ((on_or_off = *string) &&
  1975.       (on_or_off == '-' || on_or_off == '+'))
  1976.     {
  1977.       int i = 1;
  1978.       while (flag_name = string[i++])
  1979.         {
  1980.           if (flag_name == '?')
  1981.         {
  1982.           /* Print all the possible flags. */
  1983.         }
  1984.           else
  1985.         {
  1986.           if (change_flag_char (flag_name, on_or_off) == FLAG_ERROR)
  1987.             {
  1988.               builtin_error ("%c%c: bad option", on_or_off, flag_name);
  1989.               return (EXECUTION_FAILURE);
  1990.             }
  1991.         }
  1992.         }
  1993.     }
  1994.       else
  1995.     {
  1996.       break;
  1997.     }
  1998.       list = list->next;
  1999.     }
  2000.  
  2001.   /* Assigning $1 ... $n */
  2002.   if (list)
  2003.     remember_args (list, 1);
  2004.   return (EXECUTION_SUCCESS);
  2005. }
  2006.  
  2007.  
  2008. /* **************************************************************** */
  2009. /*                                    */
  2010. /*            Pushing and Popping a Context            */
  2011. /*                                    */
  2012. /* **************************************************************** */
  2013.  
  2014. WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
  2015. int dollar_arg_stack_slots = 0;
  2016. int dollar_arg_stack_index = 0;
  2017.  
  2018. push_context ()
  2019. {
  2020.   extern int variable_context;
  2021.  
  2022.   push_dollar_vars ();
  2023.   variable_context++;
  2024. }
  2025.  
  2026. pop_context ()
  2027. {
  2028.   extern int variable_context;
  2029.  
  2030.   pop_dollar_vars ();
  2031.   kill_all_local_variables ();
  2032.   variable_context--;
  2033. }
  2034.  
  2035. /* Save the existing arguments on a stack. */
  2036. push_dollar_vars ()
  2037. {
  2038.   extern WORD_LIST *list_rest_of_args ();
  2039.  
  2040.   if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
  2041.     {
  2042.       if (!dollar_arg_stack)
  2043.     {
  2044.       dollar_arg_stack_slots = 10;
  2045.       dollar_arg_stack =
  2046.         (WORD_LIST **)xmalloc (dollar_arg_stack_slots * sizeof (WORD_LIST *));
  2047.     }
  2048.       else
  2049.     {
  2050.       dollar_arg_stack_slots += 10;
  2051.       dollar_arg_stack =
  2052.         (WORD_LIST **)xrealloc (dollar_arg_stack,
  2053.                     dollar_arg_stack_slots * sizeof (WORD_LIST **));
  2054.     }
  2055.     }
  2056.   dollar_arg_stack[dollar_arg_stack_index] = list_rest_of_args ();
  2057.   dollar_arg_stack[++dollar_arg_stack_index] = (WORD_LIST *)NULL;
  2058. }
  2059.  
  2060. pop_dollar_vars ()
  2061. {
  2062.   if (!dollar_arg_stack || !dollar_arg_stack_index)
  2063.     return;
  2064.  
  2065.   remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
  2066.   dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
  2067.   dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
  2068. }
  2069.  
  2070. /* Remember LIST in $0 ... $9, and REST_OF_ARGS.  If DESTRUCTIVE is
  2071.    non-zero, then discard whatever the existing arguments are, else
  2072.    only discard the ones that are to be replaced. */
  2073. remember_args (list, destructive)
  2074.      WORD_LIST *list;
  2075.      int destructive;
  2076. {
  2077.   register int i;
  2078.  
  2079.   for (i = 1; i < 10; i++)
  2080.     {
  2081.       if (destructive && dollar_vars[i])
  2082.     {
  2083.       free (dollar_vars[i]);
  2084.       dollar_vars[i] = (char *)NULL;
  2085.     }
  2086.  
  2087.       if (list)
  2088.     {
  2089.       if (!destructive && dollar_vars[i])
  2090.         free (dollar_vars[i]);
  2091.  
  2092.       dollar_vars[i] = savestring (list->word->word);
  2093.       list = list->next;
  2094.     }
  2095.     }
  2096.  
  2097.   /* If arguments remain, assign them to REST_OF_ARGS. */
  2098.   if (!list)
  2099.     {
  2100.       dispose_words (rest_of_args);
  2101.       rest_of_args = NULL;
  2102.     }
  2103.   else
  2104.     {
  2105.       rest_of_args = (WORD_LIST *)copy_word_list (list);
  2106.     }
  2107. }
  2108.  
  2109. /* Return if LIST is NULL else barf and jump to top_level. */
  2110. no_args (list)
  2111.      WORD_LIST *list;
  2112. {
  2113.   if (list)
  2114.     {
  2115.       builtin_error ("extra arguments");
  2116.       longjmp (top_level, DISCARD);
  2117.     }
  2118. }
  2119.  
  2120. /* Shift the arguments ``left''.  Shift DOLLAR_VARS down then take one
  2121.    off of REST_OF_ARGS and place it into DOLLAR_VARS[9].  If LIST has
  2122.    anything in it, it is a number which says where to start the shifting. */
  2123. shift_builtin (list)
  2124.      WORD_LIST *list;
  2125. {
  2126.   int times = get_numeric_arg (list);
  2127.  
  2128.   while (times-- > 0) {
  2129.     register int count;
  2130.  
  2131.     if (dollar_vars[1]) free (dollar_vars[1]);
  2132.  
  2133.     for (count = 1; count < 9; count++)
  2134.       dollar_vars[count] = dollar_vars[count + 1];
  2135.  
  2136.     if (rest_of_args) {
  2137.       WORD_LIST *temp = rest_of_args;
  2138.  
  2139.       dollar_vars[9] = savestring (temp->word->word);
  2140.       rest_of_args = rest_of_args->next;
  2141.       dispose_word (temp->word);
  2142.     } else {
  2143.       dollar_vars[9] = (char *)NULL;
  2144.     }
  2145.   }
  2146. }
  2147.  
  2148. /* TEST/[ builtin. */
  2149. test_builtin (list)
  2150.      WORD_LIST *list;
  2151. {
  2152.   char **argv;
  2153.   int argc, result;
  2154.   WORD_LIST *t = list;
  2155.  
  2156.   /* We let Matthew Bradburn and Kevin Braunsdorf's code do the
  2157.      actual test command.  So turn the list of args into an array
  2158.      of strings, since that is what his code wants. */
  2159.  
  2160.   if (!list)
  2161.     {
  2162.       if (strcmp (this_command_name, "[") == 0)
  2163.     fprintf (stderr, "[: missing `]'\n");
  2164.  
  2165.       return (EXECUTION_FAILURE);
  2166.     }
  2167.  
  2168.   /* Get the length of the argument list. */
  2169.   for (argc = 0; t; t = t->next, argc++);
  2170.  
  2171.   /* Account for argv[0] being a command name.  This makes our life easier. */
  2172.   argc++;
  2173.   argv = (char **)xmalloc ((1 + argc) * sizeof (char *));
  2174.   argv[argc] = (char *)NULL;
  2175.  
  2176.   /* this_command_name is the name of the command that invoked this
  2177.      function.  So you can't call test_builtin () directly from
  2178.      within this code, there are too many things to worry about. */
  2179.   argv[0] = savestring (this_command_name);
  2180.  
  2181.   for (t = list, argc = 1; t; t = t->next, argc++)
  2182.     argv[argc] = savestring (t->word->word);
  2183.  
  2184.   result = test_command (argc, argv);
  2185.   free_array (argv);
  2186.   return (result);
  2187. }
  2188.  
  2189. /* Print the totals for system and user time used.  The
  2190.    information comes from variables in jobs.c used to keep
  2191.    track of this stuff. */
  2192.  
  2193. #if !defined (SYSV)
  2194. static long
  2195. scale60 (tvalp)
  2196.      struct timeval *tvalp;
  2197. {
  2198.   return (tvalp->tv_sec * 60 + tvalp->tv_usec / 16667);
  2199. }
  2200. #endif /* !SYSV */
  2201.  
  2202. times_builtin (list)
  2203.      WORD_LIST *list;
  2204. {
  2205.   int system_minutes_used, user_minutes_used;
  2206.   long system_seconds_used, user_seconds_used;
  2207. #ifndef SYSV
  2208.   struct rusage self, kids;
  2209.  
  2210.   no_args (list);
  2211.  
  2212.   getrusage (RUSAGE_SELF, &self);
  2213.   getrusage (RUSAGE_CHILDREN, &kids);    /* terminated child processes */
  2214.  
  2215.   user_seconds_used = scale60(&self.ru_utime) + scale60(&kids.ru_utime);
  2216.   system_seconds_used = scale60(&self.ru_stime) + scale60(&kids.ru_stime);
  2217.  
  2218. #else /* SYSV */
  2219.  
  2220. #ifndef HZ
  2221. #define HZ 100        /* From my Sys V.3.2 manual for times(2) */
  2222. #endif /* !HZ */
  2223.  
  2224.   struct tms t;
  2225.   int sys_seconds, user_seconds;
  2226.  
  2227.   no_args (list);
  2228.  
  2229.   times (&t);
  2230.  
  2231.   /* As of System V.3, HP-UX 6.5, and other ATT-like systems, this stuff is
  2232.      returned in terms of clock ticks (HZ from sys/param.h).  C'mon, guys.
  2233.      This kind of stupid clock-dependent stuff is exactly the reason 4.2BSD
  2234.      introduced the `timeval' struct. */
  2235.  
  2236.   system_seconds_used = ((t.tms_stime + HZ - 1) / HZ) +
  2237.             ((t.tms_cstime + HZ - 1) / HZ);
  2238.  
  2239.   user_seconds_used   = ((t.tms_utime + HZ - 1) / HZ) +
  2240.             ((t.tms_cutime + HZ - 1) / HZ);
  2241.  
  2242. #endif /* SYSV */
  2243.  
  2244.   system_minutes_used = system_seconds_used / 60;
  2245.   system_seconds_used %= 60;
  2246.  
  2247.   user_minutes_used = user_seconds_used / 60;
  2248.   user_seconds_used %= 60;
  2249.  
  2250.   printf ("%0dm%0ds %0dm%0ds\n", system_minutes_used, system_seconds_used,
  2251.                    user_minutes_used, user_seconds_used);
  2252.   return (EXECUTION_SUCCESS);
  2253. }
  2254.  
  2255. /* The trap command:
  2256.  
  2257.    trap <arg> <signal ...>
  2258.    trap <signal ...>
  2259.    trap -l
  2260.    trap
  2261.  
  2262.    Set things up so that ARG is executed when SIGNAL(s) N is recieved.
  2263.    If ARG is the empty string, then ignore the SIGNAL(s).  If there is
  2264.    no ARG, then set the trap for SIGNAL(s) to its original value.  Just
  2265.    plain "trap" means to print out the list of commands associated with
  2266.    each signal number.  Single arg of "-l" means list the signal names. */
  2267.  
  2268. /* Possible operations to perform on the list of signals.*/
  2269. #define SET 0            /* Set this signal to first_arg. */
  2270. #define REVERT 1        /* Revert to this signals original value. */
  2271. #define IGNORE 2        /* Ignore this signal. */
  2272.  
  2273. trap_builtin (list)
  2274.      WORD_LIST *list;
  2275. {
  2276.   register int i;
  2277.  
  2278.   if (!list)
  2279.     {
  2280.       for (i = 0; i < NSIG; i++)
  2281.     if ((int)(trap_list[i]) > 0)
  2282.       printf ("%d:%s: #(%s)\n", i, trap_list[i], signal_name (i));
  2283.       return (EXECUTION_SUCCESS);
  2284.     }
  2285.   else
  2286.     {
  2287.       char *first_arg = list->word->word;
  2288.       int operation = SET, any_failed = 0;
  2289.  
  2290.       if (strcmp ("-l", first_arg) == 0)
  2291.     {
  2292.       int column = 0;
  2293.  
  2294.       for (i = 0; i < NSIG; i++)
  2295.         {
  2296.           printf ("%d) %s", i, signal_name (i));
  2297.           if (++column < 4)
  2298.         printf ("\t");
  2299.           else
  2300.         {
  2301.           printf ("\n");
  2302.           column = 0;
  2303.         }
  2304.         }
  2305.       if (column != 0)
  2306.         printf ("\n");
  2307.       return (EXECUTION_SUCCESS);
  2308.     }
  2309.  
  2310.       if (signal_object_p (first_arg))
  2311.     operation = REVERT;
  2312.       else
  2313.     {
  2314.       list = list->next;
  2315.       if (*first_arg == '\0')
  2316.         operation = IGNORE;
  2317.     }
  2318.  
  2319.       while (list)
  2320.     {
  2321.       int signal = decode_signal (list->word->word);
  2322.  
  2323.       if (signal == NO_SIG)
  2324.         {
  2325.           builtin_error ("%s: not a signal specification", list->word->word);
  2326.           any_failed++;
  2327.         }
  2328.       else
  2329.         {
  2330.           switch (operation)
  2331.         {
  2332.         case SET:
  2333.           set_signal (signal, first_arg);
  2334.           break;
  2335.  
  2336.         case REVERT:
  2337.           restore_default_signal (signal);
  2338.           break;
  2339.  
  2340.         case IGNORE:
  2341.           ignore_signal (signal);
  2342.           break;
  2343.         }
  2344.         }
  2345.       list = list->next;
  2346.     }
  2347.       return ((!any_failed) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
  2348.     }
  2349. }
  2350.  
  2351. /* For each word in LIST, find out what the shell is going to do with
  2352.    it as a simple command. i.e., which file would this shell use to
  2353.    execve, or if it is a builtin command, or an alias.  Possible flag
  2354.    arguments:
  2355.        -type        Returns the "type" of the object, one of
  2356.             `alias', `function', `builtin', or `file'.
  2357.  
  2358.     -path        Returns the pathname of the file if -type is
  2359.             a file.
  2360.  
  2361.     -all        Returns all occurrences of words, whether they
  2362.              be a filename in the path, alias, function,
  2363.             or builtin.
  2364.    Order of evaluation:
  2365.        alias
  2366.     keyword
  2367.     function
  2368.     builtin
  2369.     file
  2370.  */
  2371. type_builtin (list)
  2372.      WORD_LIST *list;
  2373. {
  2374.   extern Function *find_shell_builtin ();
  2375.   extern char *user_command_matches (), *find_user_command ();
  2376.   extern struct { char *word; int token; } token_word_alist[];
  2377.   int path_only, type_only, all;
  2378.   int found_something = 0;
  2379.   int found_file = 0;
  2380.  
  2381.   path_only = type_only = all = 0;
  2382.  
  2383.   while (list && *(list->word->word) == '-')
  2384.     {
  2385.       char *flag = &(list->word->word[1]);
  2386.  
  2387.       if (strcmp (flag, "type") == 0)
  2388.     {
  2389.       type_only = 1;
  2390.       path_only = 0;
  2391.     }
  2392.       else if (strcmp (flag, "path") == 0 || strcmp (flag, "n") == 0)
  2393.     {
  2394.       path_only = 1;
  2395.       type_only = 0;
  2396.     }
  2397.       else if (strcmp (flag, "all") == 0)
  2398.     {
  2399.       all = 1;
  2400.     }
  2401.       else
  2402.     {
  2403.       builtin_error ("Bad flag `%s'", flag);
  2404.       return (EXECUTION_FAILURE);
  2405.     }
  2406.       list = list->next;
  2407.     }
  2408.  
  2409.   while (list)
  2410.     {
  2411.       char *command = list->word->word;
  2412.       SHELL_VAR *var = find_variable (command);
  2413.       int hashed = 0;
  2414.       char *full_path = (char *)NULL;
  2415.  
  2416. #ifdef ALIAS
  2417.       /* Command is an alias? */
  2418.       ASSOC *alias = find_alias (command);
  2419.       if (alias)
  2420.     {
  2421.       if (type_only)
  2422.         printf ("alias\n");
  2423.       else if (!path_only)
  2424.         printf ("%s is aliased to `%s'\n", command, alias->value);
  2425.       found_something++;
  2426.       if (!all)
  2427.         goto next_item;
  2428.     }
  2429. #endif
  2430.  
  2431.       /* Command is a shell reserved word? */
  2432.       {
  2433.     register int i;
  2434.  
  2435.     for (i = 0; token_word_alist[i].word; i++)
  2436.       {
  2437.         if (strcmp (token_word_alist[i].word, command) == 0)
  2438.           {
  2439.         if (type_only)
  2440.           printf ("reserved word\n");
  2441.         else if (!path_only)
  2442.           printf ("%s is a shell reserved word\n", command);
  2443.  
  2444.         found_something++;
  2445.  
  2446.         if (!all)
  2447.           goto next_item;
  2448.  
  2449.         break;
  2450.           }
  2451.       }
  2452.       }
  2453.         
  2454.       /* Command is a function? */
  2455.       if (var && function_p (var))
  2456.     {
  2457.       if (type_only)
  2458.         printf ("function\n");
  2459.       else if (!path_only)
  2460.         {
  2461.           printf ("%s is a function\n", command);
  2462.           printf ("%s () ", command);
  2463.           print_command (function_cell (var));
  2464.           printf ("\n");
  2465.         }
  2466.       found_something++;
  2467.       if (!all)
  2468.         goto next_item;
  2469.     }
  2470.  
  2471.       /* Command is a builtin? */
  2472.       if (find_shell_builtin (command))
  2473.     {
  2474.       if (type_only)
  2475.         printf ("builtin\n");
  2476.       else if (!path_only)
  2477.         printf ("%s is a shell builtin\n", command);
  2478.       found_something++;
  2479.       if (!all)
  2480.         goto next_item;
  2481.     }
  2482.  
  2483.       full_path = (char *)NULL;
  2484.  
  2485.       /* Command is a disk file? */
  2486.       if (absolute_pathname (command))
  2487.     {
  2488.       full_path = savestring (command);
  2489.     }
  2490.       else
  2491.     {
  2492.       /* If the user isn't doing "-all", then we might care about
  2493.          whether the file is present in our hash table. */
  2494.       if (!all)
  2495.         {
  2496.           if ((full_path = find_hashed_filename (command)) != (char *)NULL)
  2497.         {
  2498.           hashed++;
  2499.           full_path = savestring (full_path);
  2500.         }
  2501.           else
  2502.         {
  2503.           full_path = find_user_command (command);
  2504.         }
  2505.         }
  2506.     }
  2507.       if (all)
  2508.     {
  2509.       /* If full_path was set then it is an absolute path name. */
  2510.  
  2511.       if (full_path)
  2512.         {
  2513.           found_something++;
  2514.           if (type_only)
  2515.         printf ("file\n");
  2516.           else if (path_only)
  2517.         printf ("%s\n", full_path);
  2518.           else
  2519.         printf ("%s is %s\n", command, full_path);
  2520.           
  2521.           free (full_path);
  2522.         }
  2523.       else
  2524.         while (full_path = user_command_matches (command, 1, found_file))
  2525.           {
  2526.         found_something++;
  2527.         found_file++;
  2528.         
  2529.         if (type_only)
  2530.           printf ("file\n");
  2531.         else if (path_only)
  2532.           printf ("%s\n", full_path);
  2533.         else
  2534.           printf ("%s is %s\n", command, full_path);
  2535.         
  2536.         free (full_path);
  2537.           }
  2538.       
  2539.       if (!found_something && !path_only)
  2540.         printf ("%s not found\n", command);
  2541.  
  2542.       goto next_item;
  2543.     }
  2544.  
  2545.       /* We can only get here if "-all" was not specified. */
  2546.       if (!full_path)
  2547.     {
  2548.       if (!type_only && !path_only)
  2549.         printf ("%s not found\n", command);
  2550.     }
  2551.       else
  2552.     {
  2553.       if (type_only)
  2554.         printf ("file\n");
  2555.       else if (path_only)
  2556.         printf ("%s\n", full_path);
  2557.       else
  2558.         {
  2559.           if (hashed)
  2560.         printf ("%s is hashed (%s)\n", command, full_path);
  2561.           else
  2562.         printf ("%s is %s\n", command, full_path);
  2563.         }
  2564.  
  2565.       free (full_path);
  2566.     }
  2567.     next_item:
  2568.       list = list->next;
  2569.       }
  2570.   fflush (stdout);
  2571.   return (found_something ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
  2572. }
  2573.  
  2574. /* Declare or change variable attributes. */
  2575. declare_builtin (list)
  2576.      register WORD_LIST *list;
  2577. {
  2578.   return (declare_internal (list, 0));
  2579. }
  2580.  
  2581. local_builtin (list)
  2582.      register WORD_LIST *list;
  2583. {
  2584.   extern int variable_context;
  2585.  
  2586.   if (variable_context)
  2587.     return (declare_internal (list, 1));
  2588.   else
  2589.     {
  2590.       builtin_error ("Can only be used in a function");
  2591.       return (EXECUTION_FAILURE);
  2592.     }
  2593. }
  2594.  
  2595. declare_internal (list, no_modifiers)
  2596.      register WORD_LIST *list;
  2597.      int no_modifiers;
  2598. {
  2599.   extern int variable_context, array_needs_making;
  2600.   int flags_on = 0, flags_off = 0;
  2601.   int any_failed = 0;
  2602.  
  2603.   while (list)
  2604.     {
  2605.       register char *t = list->word->word;
  2606.       int *flags;
  2607.  
  2608.       if (*t != '+' && *t != '-')
  2609.     break;
  2610.  
  2611.       if (no_modifiers)
  2612.     {
  2613.       builtin_error ("Modifiers not allowed");
  2614.       return (EXECUTION_FAILURE);
  2615.     }
  2616.  
  2617.       if (*t == '+')
  2618.     flags = &flags_off;
  2619.       else
  2620.     flags = &flags_on;
  2621.  
  2622.       t++;
  2623.  
  2624.       while (*t)
  2625.     {
  2626.       if (*t == 'f')
  2627.         *flags |= att_function, t++;
  2628.       else if (*t == 'x')
  2629.         *flags |= att_exported, t++, array_needs_making = 1;
  2630.       else if (*t == 'r')
  2631.         *flags |= att_readonly, t++;
  2632.       else
  2633.         {
  2634.           builtin_error ("Bad flag `-%c'", *t);
  2635.           return (EXECUTION_FAILURE);
  2636.         }
  2637.     }
  2638.  
  2639.       list = list->next;
  2640.     }
  2641.  
  2642.   /* If there are no more arguments left, then we just want to show
  2643.      some variables. */
  2644.   if (!list)
  2645.     {
  2646.       /* If we didn't allow modifiers, then this is the `local' command.
  2647.      Perhaps the flag should be called `local_command' instead of
  2648.      `no_modifiers'.  At any rate, only show local variables defined
  2649.      at this context level. */
  2650.       if (no_modifiers)
  2651.     {
  2652.       extern SHELL_VAR *variable_list;
  2653.       register SHELL_VAR *vlist = variable_list;
  2654.  
  2655.       while (vlist)
  2656.         {
  2657.           if (!invisible_p (vlist) && vlist->context == variable_context)
  2658.         print_assignment (vlist);
  2659.           vlist = vlist->next;
  2660.         }
  2661.     }
  2662.       else
  2663.     {
  2664.       if (!flags_on)
  2665.         set_builtin ((WORD_LIST *)NULL);
  2666.       else
  2667.         set_or_show_attributes ((WORD_LIST *)NULL, flags_on);
  2668.     }
  2669.  
  2670.       fflush (stdout);
  2671.       return (EXECUTION_SUCCESS);
  2672.     }
  2673.  
  2674.   /* There are arguments left, so we are making variables. */
  2675.   while (list)
  2676.     {
  2677.       char *value, *name = savestring (list->word->word);
  2678.       int offset = assignment (name);
  2679.  
  2680.       if (offset)
  2681.     {
  2682.       name[offset] = '\0';
  2683.       value = name + offset + 1;
  2684.     }
  2685.       else
  2686.     {
  2687.       value = "";
  2688.     }
  2689.  
  2690.       /* If VARIABLE_CONTEXT has a non-zero value, then we are executing
  2691.      inside of a function.  This means we should make local variables,
  2692.      not global ones. */
  2693.  
  2694.       if (variable_context)
  2695.     make_local_variable (name);
  2696.  
  2697.       /* If we are declaring a function, then complain about it in some way.
  2698.      We don't let people make functions by saying `typeset -f foo=bar'. */
  2699.  
  2700.       if (flags_on & att_function)
  2701.     {
  2702.       builtin_error ("Can't use `-f' to make functions");
  2703.       return (EXECUTION_FAILURE);
  2704.     }
  2705.       else
  2706.     {
  2707.       SHELL_VAR *bind_variable (), *find_variable (), *v;
  2708.  
  2709.       if ((v = find_variable (name)) == (SHELL_VAR *)NULL)
  2710.         v = bind_variable (name, "");
  2711.  
  2712.       /* We are not allowed to rebind readonly variables that
  2713.          already are readonly unless we are turning off the
  2714.          readonly bit. */
  2715.       if (flags_off & att_readonly)
  2716.         flags_on &= ~att_readonly;
  2717.  
  2718.       if (value && readonly_p (v) && (!(flags_off & att_readonly)))
  2719.         {
  2720.           builtin_error ("%s: readonly variable", name);
  2721.           any_failed++;
  2722.           goto hack_next_variable;
  2723.         }
  2724.         
  2725.       v->attributes |= flags_on;
  2726.       v->attributes &= ~flags_off;
  2727.  
  2728.       if (offset)
  2729.         {
  2730.           free (v->value);
  2731.           v->value = savestring (value);
  2732.         }
  2733.     }
  2734.  
  2735.       stupidly_hack_special_variables (name);
  2736.  
  2737.     hack_next_variable:
  2738.       free (name);
  2739.       list = list->next;
  2740.     }
  2741.   return ((!any_failed) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
  2742. }
  2743.  
  2744. /* Set or display the mask used by the system when creating files. */
  2745. umask_builtin (list)
  2746.      WORD_LIST *list;
  2747. {
  2748.   if (list)
  2749.     {
  2750.       int new_umask;
  2751.       new_umask = read_octal (list->word->word);
  2752.  
  2753.       /* Note that other shells just let you set the umask to zero
  2754.      by specifying a number out of range.  This is a problem
  2755.      with those shells.  We don't change the umask if the input
  2756.      is lousy. */
  2757.       if (new_umask == -1)
  2758.     {
  2759.       builtin_error ("`%s' is not an octal number from 000 to 777",
  2760.              list->word->word);
  2761.       return (EXECUTION_FAILURE);
  2762.     }
  2763.       umask (new_umask);
  2764.     }
  2765.   else
  2766.     {
  2767.       /* Display the UMASK for this user. */
  2768.       int old_umask = umask (022);
  2769.       umask (old_umask);
  2770.       printf ("%03o\n", old_umask);
  2771.     }
  2772.   fflush (stdout);
  2773.   return (EXECUTION_SUCCESS);
  2774. }
  2775.  
  2776. /* Return the octal number parsed from STRING, or -1 to indicate
  2777.    that the string contained a bad number. */
  2778. int
  2779. read_octal (string)
  2780.      char *string;
  2781. {
  2782.   int result = 0;
  2783.   int digits = 0;
  2784.   while (*string && *string >= '0' && *string < '8')
  2785.     {
  2786.       digits++;
  2787.       result = (result * 8) + *string++ - '0';
  2788.     }
  2789.  
  2790.   if (!digits || result > 0777 || *string)
  2791.     result = -1;
  2792.  
  2793.   return (result);
  2794. }
  2795.  
  2796. /* Remove the variables specified in LIST from VARIABLE_LIST. */
  2797. unset_builtin (list)
  2798.   WORD_LIST *list;
  2799. {
  2800.   extern char **non_unsettable_vars;
  2801.   int unset_function = 0;
  2802.   int any_failed = 0;
  2803.  
  2804.   while (list)
  2805.     {
  2806.       char *name = list->word->word;
  2807.  
  2808.       if (strcmp (name, "-f") == 0)
  2809.     {
  2810.       list = list->next;
  2811.       unset_function++;
  2812.       continue;
  2813.     }
  2814.  
  2815.       if (find_name_in_list (name, non_unsettable_vars) > -1)
  2816.     {
  2817.       builtin_error ("%s: cannot unset", name);
  2818.       any_failed++;
  2819.     }
  2820.       else
  2821.     {
  2822.       int tem;
  2823.  
  2824.       if (unset_function)
  2825.         tem = unbind_function (list->word->word);
  2826.       else
  2827.         tem = unbind_variable (list->word->word);
  2828.  
  2829.       if (tem == -1)
  2830.         any_failed++;
  2831.       else
  2832.         stupidly_hack_special_variables (list->word->word);
  2833.     }
  2834.       list = list->next;
  2835.     }
  2836.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  2837. }
  2838.  
  2839. /* Run the command mentioned in list directly, without going through the
  2840.    normal alias/function/builtin/filename lookup process. */
  2841. builtin_builtin (list)
  2842.      WORD_LIST *list;
  2843. {
  2844.   extern Function *find_shell_builtin ();
  2845.   Function *function;
  2846.   register char *command;
  2847.  
  2848.   if (!list)
  2849.     return (EXECUTION_SUCCESS);
  2850.  
  2851.   command = (list->word->word);
  2852.   function = find_shell_builtin (command);
  2853.  
  2854.   if (!function)
  2855.     {
  2856.       builtin_error ("%s: Not a shell builtin!", command);
  2857.       return (EXECUTION_FAILURE);
  2858.     }
  2859.   else
  2860.     {
  2861.       this_command_name = command;
  2862.       list = list->next;
  2863.       return ((*function) (list));
  2864.     }
  2865. }
  2866.  
  2867. decrement_variable (var)
  2868.      int *var;
  2869. {
  2870.   *var = *var - 1;
  2871. }
  2872.  
  2873. /* Run the commands mentioned in LIST without paying attention to shell
  2874.    functions. */
  2875. int ignore_function_references = 0;
  2876.  
  2877. command_builtin (list)
  2878.      WORD_LIST *list;
  2879. {
  2880.   char *string, *string_list ();
  2881.   int result;
  2882.  
  2883.   begin_unwind_frame ("command_builtin");
  2884.   add_unwind_protect (decrement_variable, &ignore_function_references);
  2885.   ignore_function_references++;
  2886.  
  2887.   string = string_list (list);
  2888.  
  2889.   if (!string)
  2890.     result = EXECUTION_SUCCESS;
  2891.   else
  2892.     result = parse_and_execute (string, "command");
  2893.  
  2894.   run_unwind_frame ("command_builtin");
  2895.  
  2896.   return (result);
  2897. }
  2898.  
  2899. char tdir[MAXPATHLEN];
  2900. /* Return a pretty pathname.  If the first part of the pathname is
  2901.    the same as $HOME, then replace that with `~'.  */
  2902. char *
  2903. polite_directory_format (name)
  2904.      char *name;
  2905. {
  2906.   char *home = get_string_value ("HOME");
  2907.   int l = home ? strlen (home) : 0;
  2908.  
  2909.   if (l > 1 && strncmp (home, name, l) == 0 && (!name[l] || name[l] == '/'))
  2910.     {
  2911.       strcpy (tdir + 1, name + l);
  2912.       tdir[0] = '~';
  2913.       return (tdir);
  2914.     }
  2915.   else
  2916.     return (name);
  2917. }
  2918.  
  2919. #ifdef PUSHD_AND_POPD
  2920.  
  2921. /* Some useful commands whose behaviour has been observed in csh. */
  2922.  
  2923. /* The list of remembered directories. */
  2924. char **pushd_directory_list = (char **)NULL;
  2925.  
  2926. /* Number of existing slots in this list. */
  2927. int directory_list_size = 0;
  2928.  
  2929. /* Offset to the end of the list. */
  2930. int directory_list_offset = 0;
  2931.  
  2932. pushd_builtin (list)
  2933.      WORD_LIST *list;
  2934. {
  2935.   char *temp, *current_directory, *get_working_directory ();
  2936.   int j = directory_list_offset - 1;
  2937.   char direction = '+';
  2938.  
  2939.   /* If there is no argument list then switch current and
  2940.      top of list. */
  2941.   if (!list)
  2942.     {
  2943.       if (!directory_list_offset)
  2944.     {
  2945.       builtin_error ("No other directory");
  2946.       return (EXECUTION_FAILURE);
  2947.     }
  2948.  
  2949.       current_directory = get_working_directory ("pushd");
  2950.       if (!current_directory)
  2951.     return (EXECUTION_FAILURE);
  2952.  
  2953.       temp = pushd_directory_list[j];
  2954.       pushd_directory_list[j] = current_directory;
  2955.       goto change_to_temp;
  2956.     }
  2957.   else
  2958.     {
  2959.       direction = *(list->word->word);
  2960.       if (direction == '+' || direction == '-')
  2961.     {
  2962.       int num;
  2963.       if (1 == sscanf (&(list->word->word)[1], "%d", &num))
  2964.         {
  2965.           if (direction == '-')
  2966.         num = directory_list_offset - num;
  2967.  
  2968.           if (num > directory_list_offset)
  2969.         {
  2970.           if (!directory_list_offset)
  2971.             builtin_error ("Directory stack empty");
  2972.           else
  2973.             builtin_error ("Stack contains only %d directories",
  2974.                   directory_list_offset + 1);
  2975.           return (EXECUTION_FAILURE);
  2976.         }
  2977.           else
  2978.         {
  2979.           /* Rotate the stack num times.  Remember, the
  2980.              current directory acts like it is part of the
  2981.              stack. */
  2982.           temp = get_working_directory ("pushd");
  2983.  
  2984.           if (!num)
  2985.             goto change_to_temp;
  2986.  
  2987.           do
  2988.             {
  2989.               char *top =
  2990.             pushd_directory_list[directory_list_offset - 1];
  2991.  
  2992.               for (j = directory_list_offset - 2; j > -1; j--)
  2993.             pushd_directory_list[j + 1] = pushd_directory_list[j];
  2994.  
  2995.               pushd_directory_list[j + 1] = temp;
  2996.  
  2997.               temp = top;
  2998.               num--;
  2999.             }
  3000.           while (num);
  3001.  
  3002.           temp = savestring (temp);
  3003.         change_to_temp:
  3004.           {
  3005.             int tt = EXECUTION_FAILURE;
  3006.  
  3007.             if (temp)
  3008.               {
  3009.             tt = cd_to_string (temp);
  3010.             free (temp);
  3011.               }
  3012.  
  3013.             if ((tt == EXECUTION_SUCCESS) &&
  3014.             (!find_variable ("pushd_silent")))
  3015.               dirs_builtin ((WORD_LIST *)NULL);
  3016.  
  3017.             return (tt);
  3018.           }
  3019.         }
  3020.         }
  3021.     }
  3022.  
  3023.       /* Change to the directory in list->word->word.  Save the current
  3024.      directory on the top of the stack. */
  3025.       current_directory = get_working_directory ("pushd");
  3026.       if (!current_directory)
  3027.     return (EXECUTION_FAILURE);
  3028.  
  3029.       if (cd_builtin (list) == EXECUTION_SUCCESS)
  3030.     {
  3031.       if (directory_list_offset == directory_list_size)
  3032.         {
  3033.           if (!pushd_directory_list)
  3034.         pushd_directory_list =
  3035.           (char **)xmalloc ((directory_list_size = 10) * sizeof (char *));
  3036.           else
  3037.         pushd_directory_list =
  3038.           (char **)xrealloc (pushd_directory_list,
  3039.                      (directory_list_size += 10) * sizeof (char *));
  3040.         }
  3041.       pushd_directory_list[directory_list_offset++] = current_directory;
  3042.  
  3043.       if (!find_variable ("pushd_silent"))
  3044.         dirs_builtin ((WORD_LIST *)NULL);
  3045.  
  3046.       return (EXECUTION_SUCCESS);
  3047.     }
  3048.       else
  3049.     {
  3050.       free (current_directory);
  3051.       return (EXECUTION_FAILURE);
  3052.     }
  3053.     }
  3054. }
  3055.  
  3056. /* Print the current list of directories on the directory stack. */
  3057. dirs_builtin (list)
  3058.      WORD_LIST *list;
  3059. {
  3060.   register int i, format = 0;
  3061.   char *temp, *polite_directory_format (), *get_working_directory ();
  3062.  
  3063.   /* Maybe do long form? */
  3064.   if (list)
  3065.     {
  3066.       if (strcmp (list->word->word, "-l") == 0)
  3067.     format++;
  3068.  
  3069.       if (!format || list->next)
  3070.     {
  3071.       builtin_error ("usage: dirs [ -l ]");
  3072.       return (EXECUTION_FAILURE);
  3073.     }
  3074.     }
  3075.  
  3076.   /* The first directory printed is always the current working directory. */
  3077.   temp = get_working_directory ("dirs");
  3078.   if (!temp)
  3079.     temp = savestring ("<no directory>");
  3080.   printf ("%s ", format ? temp : polite_directory_format (temp));
  3081.   free (temp);
  3082.  
  3083.   /* Now print any directories in the array. */
  3084.   for (i = (directory_list_offset - 1); i > -1; i--)
  3085.     printf ("%s ", format ? pushd_directory_list[i] :
  3086.         polite_directory_format (pushd_directory_list[i]));
  3087.  
  3088.   printf ("\n");
  3089.   fflush (stdout);
  3090.   return (EXECUTION_SUCCESS);
  3091. }
  3092.  
  3093. /* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
  3094.    so if the result is EXECUTION_FAILURE then an error message has already
  3095.    been printed. */
  3096. cd_to_string (name)
  3097.      char *name;
  3098. {
  3099.   WORD_LIST *tlist = make_word_list (make_word (name), NULL);
  3100.   int result = (cd_builtin (tlist));
  3101.   dispose_words (tlist);
  3102.   return (result);
  3103. }
  3104.  
  3105. /* Pop the directory stack, and then change to the new top of the stack.
  3106.    If LIST is non-null it should consist of a word +N or -N, which says
  3107.    what element to delete from the stack.  The default is the top one. */
  3108. popd_builtin (list)
  3109.      WORD_LIST *list;
  3110. {
  3111.   register int i;
  3112.   int which = 0;
  3113.   char direction = '+';
  3114.  
  3115.   if (list)
  3116.     {
  3117.       direction = *(list->word->word);
  3118.  
  3119.       if ((direction != '+' && direction != '-') ||
  3120.       (1 != sscanf (&((list->word->word)[1]), "%d", &which)))
  3121.     {
  3122.       builtin_error ("Bad arg `%s'", list->word->word);
  3123.       return (EXECUTION_FAILURE);
  3124.     }
  3125.     }
  3126.  
  3127.   if (which > directory_list_offset || (!directory_list_offset && !which))
  3128.     {
  3129.       if (!directory_list_offset)
  3130.     builtin_error ("Directory stack empty");
  3131.       else
  3132.     builtin_error ("Stack contains only %d directories",
  3133.                directory_list_offset + 1);
  3134.       return (EXECUTION_FAILURE);
  3135.     }
  3136.  
  3137.   /* Handle case of no specification, or top of stack specification. */
  3138.    if ((direction == '+' && which == 0) ||
  3139.        (direction == '-' && which == directory_list_offset))
  3140.     {
  3141.       i = cd_to_string (pushd_directory_list[directory_list_offset - 1]);
  3142.       if (i != EXECUTION_SUCCESS)
  3143.     return (i);
  3144.       free (pushd_directory_list[--directory_list_offset]);
  3145.     }
  3146.   else
  3147.     {
  3148.       /* Since an offset other than the top directory was specified,
  3149.      remove that directory from the list and shift the remainder
  3150.      of the list into place. */
  3151.  
  3152.       if (direction == '+')
  3153.     i = directory_list_offset - which;
  3154.       else
  3155.     i = which;
  3156.  
  3157.       free (pushd_directory_list[i]);
  3158.       directory_list_offset--;
  3159.  
  3160.       /* Shift the remainder of the list into place. */
  3161.       for (; i < directory_list_offset; i++)
  3162.     pushd_directory_list[i] = pushd_directory_list[i + 1];
  3163.     }
  3164.  
  3165.   if (!find_variable ("pushd_silent"))
  3166.     dirs_builtin ((WORD_LIST *)NULL);
  3167.  
  3168.   return (EXECUTION_SUCCESS);
  3169. }
  3170.  
  3171. #endif  /* PUSHD_AND_POPD */
  3172.  
  3173. #ifdef ALIAS
  3174.  
  3175. /* Hack the alias command in a Korn shell way. */
  3176. alias_builtin (list)
  3177.      WORD_LIST *list;
  3178. {
  3179.   int any_failed = 0;
  3180.  
  3181.   if (!list)
  3182.     {
  3183.       register int i;
  3184.  
  3185.       if (!aliases)
  3186.     return (EXECUTION_FAILURE);
  3187.  
  3188.       for (i = 0; aliases[i]; i++)
  3189.     print_alias (aliases[i]);
  3190.     }
  3191.   else
  3192.     {
  3193.       while (list)
  3194.     {
  3195.       register char *value, *name = list->word->word;
  3196.       register int offset;
  3197.  
  3198.       for (offset = 0; name[offset] && name[offset] != '='; offset++);
  3199.  
  3200.       if (offset && name[offset] == '=')
  3201.         {
  3202.           name[offset] = '\0';
  3203.           value = name + offset + 1;
  3204.  
  3205.           add_alias (name, value);
  3206.         }
  3207.       else
  3208.         {
  3209.           ASSOC *t = find_alias (name);
  3210.           if (t)
  3211.         print_alias (t);
  3212.           else
  3213.         {
  3214.           if (interactive)
  3215.             builtin_error ("`%s' not found", name);
  3216.           any_failed++;
  3217.         }
  3218.         }
  3219.       list = list->next;
  3220.     }
  3221.     }
  3222.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  3223. }
  3224.  
  3225. /* Remove aliases named in LIST from the aliases database. */
  3226. unalias_builtin (list)
  3227.      register WORD_LIST *list;
  3228. {
  3229.   register ASSOC *alias;
  3230.   int any_failed = 0;
  3231.  
  3232.   while (list)
  3233.     {
  3234.       alias = find_alias (list->word->word);
  3235.  
  3236.       if (alias)
  3237.     remove_alias (alias->name);
  3238.       else
  3239.     {
  3240.       if (interactive)
  3241.         builtin_error ("`%s' not an alias", list->word->word);
  3242.  
  3243.       any_failed++;
  3244.     }
  3245.  
  3246.       list = list->next;
  3247.     }
  3248.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  3249. }
  3250.  
  3251. /* Output ALIAS in such a way as to allow it to be read back in. */
  3252. print_alias (alias)
  3253.      ASSOC *alias;
  3254. {
  3255.   register int i;
  3256.  
  3257.   for (i = 0; alias->value[i] && !whitespace (alias->value[i]); i++);
  3258.  
  3259.   if (alias->value[i])
  3260.     printf ("alias %s=\"%s\"\n", alias->name, alias->value);
  3261.   else
  3262.     printf ("alias %s=%s\n", alias->name, alias->value);
  3263.  
  3264.   fflush (stdout);
  3265. }
  3266.  
  3267. #endif  /* ALIAS */
  3268.  
  3269. /* Wait for the pid in LIST to stop or die.  If no arguments are given, then
  3270.    wait for all of the active background processes of the shell and return
  3271.    0.  If a list of pids or job specs are given, return the exit status of
  3272.    the last one waited for. */
  3273. wait_builtin (list)
  3274.      WORD_LIST *list;
  3275. {
  3276.   extern wait_for_background_pids ();
  3277.   extern wait_for_single_pid ();
  3278.   extern int job_control;
  3279.   int status = EXECUTION_SUCCESS;
  3280.  
  3281.   /* We support jobs or pids.
  3282.      wait <pid-or-job> [pid-or-job ...] */
  3283.  
  3284.   /* But wait without any arguments means to wait for all of the shell's
  3285.      currently active background processes. */
  3286.   if (!list)
  3287.     {
  3288.       wait_for_background_pids ();
  3289.       return (EXECUTION_SUCCESS);
  3290.     }
  3291.  
  3292.   while (list)
  3293.     {
  3294.       int pid;
  3295.       if (digit (*(list->word->word)))
  3296.     {
  3297.       if (sscanf (list->word->word, "%d", &pid) == 1)
  3298.         status = wait_for_single_pid (pid);
  3299.       else
  3300.         {
  3301.           builtin_error ("`%s' is not a pid or job spec", list->word->word);
  3302.           return (EXECUTION_FAILURE);
  3303.         }
  3304.     }
  3305. #ifdef JOB_CONTROL
  3306.       else if (job_control)
  3307.     /* Must be a job spec.  Check it out. */
  3308.     {
  3309.       int oldmask = sigblock (sigmask (SIGCHLD));
  3310.       int job = get_job_spec (list);
  3311.  
  3312.       if (job < 0 || job >= job_slots || !jobs[job])
  3313.         {
  3314.           if (job != DUP_JOB)
  3315.         builtin_error ("No such job %s", list->word->word);
  3316.           sigsetmask (oldmask);
  3317.           return (EXECUTION_FAILURE);
  3318.         }
  3319.  
  3320.       /* Job spec used.  Wait for the last pid in the pipeline. */
  3321.  
  3322.       sigsetmask (oldmask);
  3323.  
  3324.       status = wait_for_job (job);
  3325.     }
  3326. #endif /* JOB_CONTROL */
  3327.       else
  3328.     {
  3329.       builtin_error ("%s is not a pid or legal job spec", list->word->word);
  3330.       status = EXECUTION_FAILURE;
  3331.     }
  3332.       list = list->next;
  3333.     }
  3334.   return (status);
  3335. }
  3336.  
  3337. /* This is a lot like report_error (), but it is for shell builtins instead
  3338.    of shell control structures, and it won't ever exit the shell. */
  3339. #if defined (HAVE_VPRINTF)
  3340. /* VARARGS */
  3341. builtin_error (va_alist)
  3342.      va_dcl
  3343. {
  3344.   extern char *this_command_name;
  3345.   char *format;
  3346.   va_list args;
  3347.  
  3348.   fprintf (stderr, "%s: ", this_command_name);
  3349.   va_start (args);
  3350.   format = va_arg (args, char *);
  3351.   vfprintf (stderr, format, args);
  3352.   va_end (args);
  3353.   fprintf (stderr, "\n");
  3354. }
  3355.  
  3356. #else
  3357.  
  3358. builtin_error (format, arg1, arg2)
  3359.      char *format, *arg1, *arg2;
  3360. {
  3361.   extern char *this_command_name;
  3362.  
  3363.   fprintf (stderr, "%s: ", this_command_name);
  3364.   fprintf (stderr, format, arg1, arg2);
  3365.   fprintf (stderr, "\n");
  3366.   fflush (stderr);
  3367. }
  3368. #endif /* HAVE_VPRINTF */
  3369.  
  3370. #ifdef JOB_CONTROL
  3371. /* **************************************************************** */
  3372. /*                                    */
  3373. /*            Job Control!                    */
  3374. /*                                    */
  3375. /* **************************************************************** */
  3376.  
  3377. extern int job_control;
  3378.  
  3379. /* The `jobs' command.  Prints outs a list of active jobs.  If the
  3380.    first argument is `-l', then the process id's are printed also. */
  3381. jobs_builtin (list)
  3382.      WORD_LIST *list;
  3383. {
  3384.   int long_form = 0;
  3385.  
  3386.   if (!job_control)
  3387.     return (EXECUTION_SUCCESS);
  3388.  
  3389.   if (list && (strcmp (list->word->word, "-l") == 0))
  3390.     long_form = 1;
  3391.  
  3392.   list_jobs (long_form);
  3393.   return (EXECUTION_SUCCESS);
  3394. }
  3395.  
  3396. /* Suspend the current shell.  Not hard to do. */
  3397.  
  3398. static SigHandler *old_cont, *old_tstp;
  3399.  
  3400. /* Continue handler. */
  3401. sighandler
  3402. suspend_continue ()
  3403. {
  3404.   signal (SIGCONT, old_cont);
  3405.   signal (SIGTSTP, old_tstp);
  3406. }
  3407.  
  3408. /* Suspending the shell.  If -f is the arg, then do the suspend
  3409.    no matter what.  Otherwise, complain if a login shell. */
  3410. suspend_builtin (list)
  3411.      WORD_LIST *list;
  3412. {
  3413.   extern int shell_pgrp;
  3414.  
  3415.   if (!job_control)
  3416.     {
  3417.        builtin_error ("Cannot suspend a shell without job control");
  3418.        return (EXECUTION_FAILURE);
  3419.     }
  3420.  
  3421.   if (list)
  3422.     if (strcmp (list->word->word, "-f") == 0)
  3423.       goto do_suspend;
  3424.  
  3425.   no_args (list);
  3426.  
  3427.   if (login_shell)
  3428.     {
  3429.       builtin_error ("Can't suspend a login shell");
  3430.       longjmp (top_level, DISCARD);
  3431.     }
  3432.  
  3433. do_suspend:
  3434.   old_cont = (SigHandler *)signal (SIGCONT, suspend_continue);
  3435.   old_tstp = (SigHandler *)signal (SIGTSTP, SIG_DFL);
  3436.   killpg (shell_pgrp, SIGTSTP);
  3437.   return (EXECUTION_SUCCESS);
  3438. }
  3439.  
  3440. /* How to bring a job into the foreground. */
  3441. fg_builtin (list)
  3442.      WORD_LIST *list;
  3443. {
  3444.   int fg_bit = 1;
  3445.   register WORD_LIST *t = list;
  3446.  
  3447.   if (!job_control)
  3448.     return (EXECUTION_SUCCESS);
  3449.  
  3450.   /* If the last arg on the line is '&', then start this job in the
  3451.      background.  Else, fg the job. */
  3452.  
  3453.   while (t && t->next)
  3454.     t = t->next;
  3455.  
  3456.   if (t && strcmp (t->word->word, "&") == 0)
  3457.     fg_bit = 0;
  3458.  
  3459.   return (fg_bg (list, fg_bit));
  3460. }
  3461.  
  3462. /* How to put a job into the background. */
  3463. bg_builtin (list)
  3464.      WORD_LIST *list;
  3465. {
  3466.   if (!job_control)
  3467.     return (EXECUTION_SUCCESS);
  3468.  
  3469.   return (fg_bg (list, 0));
  3470. }
  3471.  
  3472. /* How to put a job into the foreground/background. */
  3473. fg_bg (list, foreground)
  3474.      WORD_LIST *list;
  3475.      int foreground;
  3476. {
  3477.   int job = get_job_spec (list);
  3478.   extern char *this_command_name;
  3479.  
  3480.   if (job < 0 || job >= job_slots || !jobs[job])
  3481.     {
  3482.       if (job != DUP_JOB)
  3483.     builtin_error ("No such job %s", list ? list->word->word : "");
  3484.       return (EXECUTION_FAILURE);
  3485.     }
  3486.  
  3487.   /* Or if jobs[job]->pgrp == shell_pgrp. */
  3488.   if (jobs[job]->job_control == 0)
  3489.     {
  3490.       builtin_error ("job %%%d started without job control", job + 1);
  3491.       return (EXECUTION_FAILURE);
  3492.     }
  3493.  
  3494.   if (start_job (job, foreground))
  3495.     return (EXECUTION_SUCCESS);
  3496.   else
  3497.     return (EXECUTION_FAILURE);
  3498. }
  3499.  
  3500. /* Return the job spec found in LIST. */
  3501. get_job_spec (list)
  3502.      WORD_LIST *list;
  3503. {
  3504.   register char *word;
  3505.   int job = NO_JOB;
  3506.   int substring = 0;
  3507.  
  3508.   if (!list)
  3509.     return (current_job);
  3510.  
  3511.   word = list->word->word;
  3512.  
  3513.   if (!*word)
  3514.     return (current_job);
  3515.  
  3516.   if (*word == '%')
  3517.     word++;
  3518.  
  3519.   if (digit (*word) && (sscanf (word, "%d", &job) == 1))
  3520.     return (job - 1);
  3521.  
  3522.   switch (*word) {
  3523.  
  3524.   case 0:
  3525.   case '%':
  3526.   case '+':
  3527.     return (current_job);
  3528.  
  3529.   case '-':
  3530.     return (previous_job);
  3531.  
  3532.   case '?':            /* Substring search requested. */
  3533.     substring++;
  3534.     word++;
  3535.     goto find_string;
  3536.  
  3537.   default:
  3538.   find_string:
  3539.     {
  3540.       register int i, wl = strlen (word);
  3541.       for (i = 0; i < job_slots; i++)
  3542.     {
  3543.       if (jobs[i])
  3544.         {
  3545.           register PROCESS *p = jobs[i]->pipe;
  3546.           extern char *strindex ();
  3547.           do
  3548.         {
  3549.           if ((substring && strindex (p->command, word)) ||
  3550.               (strncmp (p->command, word, wl) == 0))
  3551.             if (job != NO_JOB)
  3552.               {
  3553.             builtin_error ("ambigious job spec: %s", word);
  3554.             return (DUP_JOB);
  3555.               }
  3556.             else
  3557.               job = i;
  3558.  
  3559.           p = p->next;
  3560.         }
  3561.           while (p != jobs[i]->pipe);
  3562.         }
  3563.     }
  3564.       return (job);
  3565.     }
  3566.   }
  3567. }
  3568.  
  3569. #ifndef CONTINUE_AFTER_KILL_ERROR
  3570. #define CONTINUE_OR_FAIL return (EXECUTION_FAILURE)
  3571. #else
  3572. #define CONTINUE_OR_FAIL goto continue_killing
  3573. #endif
  3574.  
  3575. /* Here is the kill builtin.  We only have it so that people can type
  3576.    kill -KILL %1?  No, if you fill up the process table this way you
  3577.    can kill some. */
  3578. kill_builtin (list)
  3579.      WORD_LIST *list;
  3580. {
  3581.   int signal = SIGTERM;
  3582.   int pid;
  3583.  
  3584.   if (!list)
  3585.     return (EXECUTION_SUCCESS);
  3586.  
  3587.   if (strcmp (list->word->word, "-l") == 0)
  3588.     {
  3589.       register int i, column = 0;
  3590.       for (i = 1; i < NSIG; i++)
  3591.     {
  3592.       printf ("%d) %s", i, signal_name (i));
  3593.       if (++column < 4)
  3594.         printf ("\t");
  3595.       else {
  3596.         printf ("\n");
  3597.         column = 0;
  3598.       }
  3599.     }
  3600.       if (column != 0)
  3601.     printf ("\n");
  3602.       return (EXECUTION_SUCCESS);
  3603.     }
  3604.  
  3605.   /* If the user specified a signal, use that. */
  3606.   if (*(list->word->word) == '-')
  3607.     {
  3608.       signal = decode_signal (&(list->word->word)[1]);
  3609.       if (signal == NO_SIG)
  3610.     {
  3611.       builtin_error ("bad signal spec `%s'", &(list->word->word)[1]);
  3612.       return (EXECUTION_FAILURE);
  3613.     }
  3614.       else
  3615.     {
  3616.       list = list->next;
  3617.     }
  3618.     }
  3619.  
  3620.   while (list)
  3621.     {
  3622.       if (digit (*(list->word->word)))
  3623.     {
  3624.       if (sscanf (list->word->word, "%d", &pid) == 1)
  3625.         {
  3626.           if (kill_pid (pid, signal, 0) < 0)
  3627.         goto signal_error;
  3628.         }
  3629.       else
  3630.         {
  3631.           builtin_error ("No such pid %d", pid);
  3632.           CONTINUE_OR_FAIL;
  3633.         }
  3634.     }
  3635.       else if (job_control)     /* can't kill jobs if not using job control */
  3636.     {            /* Must be a job spec.  Check it out. */
  3637.       int oldmask = sigblock (sigmask (SIGCHLD));
  3638.       int job = get_job_spec (list);
  3639.  
  3640.       if (job < 0 || job >= job_slots || !jobs[job])
  3641.         {
  3642.           if (job != DUP_JOB)
  3643.         builtin_error ("No such job %s", list->word->word);
  3644.           sigsetmask (oldmask);
  3645.           CONTINUE_OR_FAIL;
  3646.         }
  3647.  
  3648.       /* Job spec used.  Kill the process group. If the job was started
  3649.          without job control, then its pgrp == shell_pgrp, so we have
  3650.          to be careful.  We take the pid of the first job in the pipeline
  3651.          in that case. */
  3652.       if (jobs[job]->job_control)
  3653.         pid = jobs[job]->pgrp;
  3654.       else
  3655.         pid = jobs[job]->pipe->pid;
  3656.  
  3657.       sigsetmask (oldmask);
  3658.  
  3659.       if (kill_pid (pid, signal, 1) < 0)
  3660.         {
  3661.         signal_error:
  3662.           if (errno == EPERM)
  3663.         builtin_error ("(%d) - Not owner", pid);
  3664.           else if (errno == ESRCH)
  3665.         builtin_error ("(%d) - No such pid", pid);
  3666.           else
  3667.         builtin_error ("Invalid signal %d", signal);
  3668.           CONTINUE_OR_FAIL;
  3669.         }
  3670.     }
  3671.       else
  3672.     {
  3673.       builtin_error ("bad process specification `%s'", list->word->word);
  3674.       CONTINUE_OR_FAIL;
  3675.     }
  3676.     continue_killing:
  3677.       list = list->next;
  3678.     }
  3679.   return (EXECUTION_SUCCESS);
  3680. }
  3681.  
  3682. #define DETACH
  3683. #ifdef DETACH
  3684. detach_builtin (list)
  3685.      WORD_LIST *list;
  3686. {
  3687.   int job = NO_JOB;
  3688.   JOB_STATE job_state;
  3689.   int oldmask;
  3690.   PROCESS *process, *pipeline;
  3691.  
  3692.   if (!job_control)
  3693.     return (EXECUTION_SUCCESS);
  3694.  
  3695.   job = get_job_spec (list);
  3696.  
  3697.   oldmask = sigblock (sigmask (SIGCHLD));
  3698.   job_state = JOBSTATE(job);
  3699.  
  3700.   killpg (jobs[job]->pgrp, SIGSTOP);
  3701.   while (jobs[job] && JOBSTATE (job) == JRUNNING)
  3702.     sigpause (0);
  3703.  
  3704.   /* Make the process group of the pipeline the same as init's.  */
  3705.   process = pipeline = jobs[job]->pipe;
  3706.   do
  3707.     {                              
  3708.       setpgrp (process->pid, 1);
  3709.       process = process -> next;
  3710.     }
  3711.   while (process != pipeline);
  3712.  
  3713.   /* Return the pipeline to the state it was in before we stopped it. */
  3714.   if (job_state == JRUNNING)
  3715.     {
  3716.       process = pipeline;         
  3717.       do
  3718.     {
  3719.       kill (process->pid, SIGCONT);
  3720.       process = process -> next;
  3721.     }
  3722.       while (process != pipeline);
  3723.     }
  3724.       
  3725.   /* Remove the job from the job list. */
  3726.   delete_job (job);
  3727.  
  3728.   sigsetmask (oldmask);
  3729. }
  3730. #endif /* DETACH */
  3731. #endif  /* JOB_CONTROL */
  3732.