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

  1. /* bashline.c -- Bash's interface to the readline library. */
  2.  
  3. /* Copyright (C) 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 <readline/readline.h>
  23. #include "config.h"
  24. #include "general.h"
  25. #include "variables.h"
  26. #include "builtins.h"
  27. #include "quit.h"
  28.  
  29. /* Called once from parse.y if we are going to use readline. */
  30. initialize_readline ()
  31. {
  32.   char **attempt_shell_completion (), *bash_tilde_expand ();
  33.   int shell_expand_line (), insert_last_arg (), bash_symbolic_link_hook ();
  34.   char *get_string_value ();
  35.  
  36.   rl_terminal_name = get_string_value ("TERM");
  37.   rl_instream = stdin, rl_outstream = stderr;
  38.   rl_special_prefixes = "$@%";
  39.  
  40.   /* Bind up our special shell functions. */
  41.   rl_add_defun ("shell-expand-line", shell_expand_line, META(CTRL('E')));
  42.   rl_add_defun ("insert-last-argument", insert_last_arg, META('.'));
  43.   rl_add_defun ("insert-last-argument", insert_last_arg, META('_'));
  44.  
  45.   /* Tell the completer that we want a crack first. */
  46.   rl_attempted_completion_function = (Function *)attempt_shell_completion;
  47.  
  48.   /* Tell the tilde expander that we want a crack if it fails. */
  49.   rl_tilde_expander = (Function *)bash_tilde_expand;
  50.  
  51.   /* Tell the completer that we might want to follow symbolic links. */
  52.   rl_symbolic_link_hook = (Function *)bash_symbolic_link_hook;
  53.  
  54.   /* And don't forget to allow conditional parsing of the ~/.inputrc
  55.      file. */
  56.   rl_readline_name = "Bash";
  57. }
  58.  
  59. /* Contains the line to push into readline. */
  60. char *push_to_readline = (char *)NULL;
  61.  
  62. /* Push the contents of push_to_readline into the
  63.    readline buffer. */
  64. bash_push_line ()
  65. {
  66.   if (push_to_readline)
  67.     {
  68.       rl_insert_text (push_to_readline);
  69.       free (push_to_readline);
  70.       push_to_readline = (char *)NULL;
  71.     }
  72. }
  73.  
  74. /* Call this to set the initial text for the next line to read
  75.    from readline. */
  76. bash_re_edit (line)
  77.      char *line;
  78. {
  79.   if (push_to_readline)
  80.     free (push_to_readline);
  81.  
  82.   push_to_readline = savestring (line);
  83.   rl_startup_hook = bash_push_line;
  84. }
  85.  
  86. /* **************************************************************** */
  87. /*                                    */
  88. /*                 Readline Stuff                  */
  89. /*                                        */
  90. /* **************************************************************** */
  91.  
  92. /* If the user requests hostname completion, then simply build a list
  93.    of hosts, and complete from that forever more. */
  94. #ifndef ETCHOSTS
  95. #define ETCHOSTS "/etc/hosts"
  96. #endif
  97.  
  98. /* The kept list of hostnames. */
  99. static char **hostname_list = (char **)NULL;
  100.  
  101. /* The physical size of the above list. */
  102. static int hostname_list_size = 0;
  103.  
  104. /* The length of the above list. */
  105. static int hostname_list_length = 0;
  106.  
  107. /* Whether or not HOSTNAME_LIST has been initialized. */
  108. int hostname_list_initialized = 0;
  109.  
  110. /* Non-zero means that HOSTNAME_LIST needs to be sorted. */
  111. static int hostname_list_needs_sorting = 0;
  112.  
  113. /* Initialize the hostname completion table. */
  114. initialize_hostname_list ()
  115. {
  116.   char *temp = get_string_value ("hostname_completion_file");
  117.  
  118.   if (!temp)
  119.     temp = ETCHOSTS;
  120.  
  121.   snarf_hosts_from_file (temp);
  122.   sort_hostname_list ();
  123.   if (hostname_list)
  124.     hostname_list_initialized++;
  125. }
  126.  
  127. /* Add NAME to the list of hosts. */
  128. add_host_name (name)
  129.      char *name;
  130. {
  131.   if (hostname_list_length + 2 > hostname_list_size)
  132.     {
  133.       if (!hostname_list)
  134.     hostname_list = (char **)xmalloc (sizeof (char *));
  135.  
  136.       hostname_list = (char **)
  137.     xrealloc (hostname_list,
  138.           (1 + (hostname_list_size += 100)) * sizeof (char *));
  139.     }
  140.  
  141.   hostname_list[hostname_list_length] = savestring (name);
  142.   hostname_list[++hostname_list_length] = (char *)NULL;
  143.   hostname_list_needs_sorting++;
  144. }
  145.  
  146. /* After you have added some names, you should sort the list of names. */
  147. sort_hostname_list ()
  148. {
  149.   extern int qsort_string_compare ();
  150.   if (hostname_list_needs_sorting && hostname_list)
  151.     qsort (hostname_list, hostname_list_length,
  152.        sizeof (char *), qsort_string_compare);
  153.   hostname_list_needs_sorting = 0;
  154. }
  155.  
  156. #define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
  157.  
  158. snarf_hosts_from_file (filename)
  159.      char *filename;
  160. {
  161.   FILE *file = fopen (filename, "r");
  162.   char *temp, buffer[256], name[256];
  163.   register int i, start;
  164.  
  165.   if (!file)
  166.     return;
  167.  
  168.   while (temp = fgets (buffer, 255, file))
  169.     {
  170.       /* Skip to first character. */
  171.       for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++);
  172.  
  173.       /* If comment, ignore. */
  174.       if (buffer[i] ==  '#')
  175.     continue;
  176.  
  177.       /* Skip internet address. */
  178.       for (; buffer[i] && !cr_whitespace (buffer[i]); i++);
  179.  
  180.       /* Gobble up names.  Each name is separated with whitespace. */
  181.       while (buffer[i] && buffer[i] != '#')
  182.     {
  183.       for (; i && cr_whitespace (buffer[i]); i++);
  184.       if (buffer[i] ==  '#')
  185.         continue;
  186.       for (start = i; buffer[i] && !cr_whitespace (buffer[i]); i++);
  187.       if ((i - start) == 0)
  188.         continue;
  189.       strncpy (name, buffer + start, i - start);
  190.       name[i - start] = '\0';
  191.       add_host_name (name);
  192.     }
  193.     }
  194.   fclose (file);
  195. }
  196.  
  197. /* Return a NULL terminated list of hostnames which begin with TEXT.
  198.    Initialize the hostname list the first time if neccessary.
  199.    The array is malloc ()'ed, but not the individual strings. */
  200. char **
  201. hostnames_matching (text)
  202.      char *text;
  203. {
  204.   register int i, len = strlen (text);
  205.   register int begin, end;
  206.   int last_search = -1;
  207.   char **result = (char **)NULL;
  208.  
  209.   if (!hostname_list_initialized)
  210.     {
  211.       initialize_hostname_list ();
  212.  
  213.       if (!hostname_list_initialized)
  214.     return ((char **)NULL);
  215.     }
  216.  
  217.   sort_hostname_list ();
  218.  
  219.   /* The list is sorted.  Do a binary search on it for the first character
  220.      in TEXT, and then grovel the names of interest. */
  221.   begin = 0; end = hostname_list_length;
  222.  
  223.   /* Special case.  If TEXT consists of nothing, then the whole list is
  224.      what is desired. */
  225.   if (!*text)
  226.     {
  227.       result = (char **)xmalloc ((1 + hostname_list_length) * sizeof (char *));
  228.       for (i = 0; i < hostname_list_length; i++)
  229.     result[i] = hostname_list[i];
  230.       result[i] = (char *)NULL;
  231.       return (result);
  232.     }
  233.  
  234.   /* Scan until found, or failure. */
  235.   while (end != begin)
  236.     {
  237.       int r;
  238.  
  239.       i = ((end - begin) / 2) + begin;
  240.       if (i == last_search)
  241.     break;
  242.  
  243.       if (hostname_list[i] &&
  244.       (r = strncmp (hostname_list[i], text, len)) == 0)
  245.     {
  246.       while (strncmp (hostname_list[i], text, len) == 0 && i) i--;
  247.       if (strncmp (hostname_list[i], text, len) != 0) i++;
  248.  
  249.       begin = i;
  250.       while (hostname_list[i] &&
  251.          strncmp (hostname_list[i], text, len) == 0) i++;
  252.       end = i;
  253.  
  254.       result = (char **)xmalloc ((1 + (end - begin)) * sizeof (char *));
  255.       for (i = 0; i + begin < end; i++)
  256.         result[i] = hostname_list[begin + i];
  257.       result[i] = (char *)NULL;
  258.       return (result);
  259.     }
  260.  
  261.       last_search = i;
  262.  
  263.       if (r < 0)
  264.     begin = i;
  265.       else
  266.     end = i;
  267.     }
  268.   return ((char **)NULL);
  269. }
  270.  
  271. /* This is a ksh-style insert-last-arg function.  The difference is that bash
  272.    puts stuff into the history file before expansion and file name generation,
  273.    so we deal with exactly what the user typed.  Those wanting the other
  274.    behavior, at least for the last arg, can use `$_'.  This also `knows' about
  275.    how rl_yank_nth_arg treats `$'. */
  276. insert_last_arg(count, c)
  277.      int count, c;
  278. {
  279.   extern int rl_explicit_arg;
  280.  
  281.   if (rl_explicit_arg)
  282.     return (rl_yank_nth_arg (count, c));
  283.   else
  284.     return (rl_yank_nth_arg ('$', c));
  285. }
  286.  
  287. /* **************************************************************** */
  288. /*                                    */
  289. /*            How To Do Shell Completion              */
  290. /*                                    */
  291. /* **************************************************************** */
  292.  
  293. /* Do some completion on TEXT.  The indices of TEXT in RL_LINE_BUFFER are
  294.    at START and END.  Return an array of matches, or NULL if none. */
  295. char **
  296. attempt_shell_completion (text, start, end)
  297.      char *text;
  298.      int start, end;
  299. {
  300.   int in_command_position = 0;
  301.   char **matches = (char **)NULL;
  302.   char *command_separator_chars = ";|&{(";
  303.  
  304.   /* Determine if this could be a command word. */
  305.   if (start == 0)
  306.     in_command_position++;
  307.   else
  308.     {
  309.       register int ti = start - 1;
  310.  
  311.       while (whitespace (rl_line_buffer[ti]) && ti > -1)
  312.     ti--;
  313.  
  314.       if (ti < 0)
  315.     in_command_position++;
  316.       else
  317.     {
  318.       if (member (rl_line_buffer[ti], command_separator_chars) ||
  319.           member (*text, command_separator_chars))
  320.         in_command_position++;
  321.     }
  322.     }
  323.  
  324.   /* Variable name? */
  325.   if (*text == '$')
  326.     {
  327.       char *variable_completion_function ();
  328.       matches = completion_matches (text, variable_completion_function);
  329.     }
  330.  
  331.   /* If the word starts in `~', and there is no slash in the word, then
  332.      try completing this word as a username. */
  333.   if (!matches && *text == '~' && !index (text, '/'))
  334.     {
  335.       char *username_completion_function ();
  336.       matches = completion_matches (text, username_completion_function);
  337.     }
  338.  
  339.   /* Another one.  Why not?  If the word starts in '@', then look through
  340.      the world of known hostnames for completion first. */
  341.   if (!matches && *text == '@')
  342.     {
  343.       char *hostname_completion_function ();
  344.       matches = completion_matches (text, hostname_completion_function);
  345.     }
  346.  
  347.   /* And last, (but not least) if this word is in a command position, then
  348.      complete over possible command names, including aliases, functions,
  349.      and command names. */
  350.  
  351.   if (!matches && in_command_position)
  352.     {
  353.       char *command_word_completion_function ();
  354.       int text_offset = 0;
  355.  
  356.       if (start && member (*text, command_separator_chars))
  357.     text_offset++, start++;
  358.  
  359.       if (*text != '/')
  360.     matches = completion_matches (&text[text_offset],
  361.                       command_word_completion_function);
  362.     }
  363.   return (matches);
  364. }
  365.  
  366. /* This is the function to call when the word to complete is at the start
  367.    of a line.  It grovels $PATH, looking for commands that match.  It also
  368.    scans for aliases, function names, and the shell_builtin table. */
  369. char *
  370. command_word_completion_function (hint_text, state)
  371.      char *hint_text;
  372.      int state;
  373. {
  374.   static char *hint = (char *)NULL;
  375.   static char *path = (char *)NULL;
  376.   static char *val = (char *)NULL;
  377.   static int path_index, hint_len, istate;
  378.   static char *filename_hint = (char *)NULL;
  379.   static int mapping_over = 0;
  380.  
  381.   static int local_index;
  382.   static SHELL_VAR *varlist;
  383.  
  384.   char *filename_completion_function ();
  385.   char *extract_colon_unit ();
  386.  
  387.   /* We have to map over the possibilities for command words.  If we have
  388.      no state, then make one just for that purpose. */
  389.  
  390.   if (!state)
  391.     {
  392.       if (hint) free (hint);
  393.  
  394.       path = get_string_value ("PATH");
  395.       path_index = 0;
  396.  
  397.       hint = savestring (hint_text);
  398.       hint_len = strlen (hint);
  399.  
  400.       mapping_over = 0;
  401.       val = (char *)NULL;
  402.  
  403.       /* Initialize the variables for each type of command word. */
  404.       local_index = 0;
  405.       varlist = variable_list;
  406.     }
  407.  
  408.   /* mapping_over says what we are currently hacking.  Note that every case
  409.      in this list must fall through when there are no more possibilities. */
  410.  
  411.   switch (mapping_over)
  412.     {
  413.     case 0:            /* aliases come first. */
  414. #ifdef ALIAS
  415.       while (aliases && aliases[local_index])
  416.     {
  417.       local_index++;
  418.       if (strncmp (aliases[local_index - 1]->name, hint, hint_len) == 0)
  419.         return (savestring (aliases[local_index - 1]->name));
  420.     }
  421.       local_index = 0;
  422. #endif                /* ALIAS */
  423.       mapping_over++;
  424.  
  425.     case 1:            /* Then function names. */
  426.       while (varlist)
  427.     {
  428.       if (function_p (varlist) && !invisible_p (varlist) &&
  429.           strncmp (varlist->name, hint, hint_len) == 0)
  430.         {
  431.           char *temp = savestring (varlist->name);
  432.           varlist = varlist->next;
  433.           return (temp);
  434.         }
  435.       else
  436.         varlist = varlist->next;
  437.     }
  438.       mapping_over++;
  439.  
  440.     case 2:            /* Then shell builtins. */
  441.       while (shell_builtins[local_index].function)
  442.     {
  443.       local_index++;
  444.       if (strncmp (shell_builtins[local_index - 1].name,
  445.                hint, hint_len) == 0)
  446.         return (savestring (shell_builtins[local_index - 1].name));
  447.     }
  448.       mapping_over++;
  449.       local_index = 0;
  450.     }
  451.  
  452.   /* Repeatadly call filename_completion_function while we have
  453.      members of PATH left.  Question:  should we stat each file?
  454.      Answer: we call executable_file () on each file. */
  455.  outer:
  456.  
  457.   istate = (val != (char *)NULL);
  458.  
  459.   if (!istate)
  460.     {
  461.       char *current_path;
  462.  
  463.       /* Get the next directory from the path.  If there is none, then we
  464.      are all done. */
  465.       if (!path ||
  466.       !path[path_index] ||
  467.       !(current_path = extract_colon_unit (path, &path_index)))
  468.     return ((char *)NULL);
  469.  
  470.       if (!*current_path)
  471.     {
  472.       free (current_path);
  473.       current_path = savestring (".");
  474.     }
  475.  
  476.       if (filename_hint)
  477.     free (filename_hint);
  478.  
  479.       filename_hint =
  480.     (char *)xmalloc (2 + strlen (current_path)
  481.                  + strlen (hint));
  482.       sprintf (filename_hint, "%s/%s", current_path, hint);
  483.  
  484.       free (current_path);
  485.     }
  486.  
  487.  inner:
  488.   val = filename_completion_function (filename_hint, istate);
  489.   istate = 1;
  490.  
  491.   if (!val)
  492.     {
  493.       goto outer;
  494.     }
  495.   else
  496.     {
  497.       char *rindex (), *temp = rindex (val, '/');
  498.       temp++;
  499.       if ((strncmp (hint, temp, hint_len) == 0) && executable_file (val))
  500.     {
  501.       temp = (savestring (temp));
  502.       free (val);
  503.       val = "";        /* So it won't be NULL */
  504.       return (temp);
  505.     }
  506.       else
  507.     {
  508.       free (val);
  509.       goto inner;
  510.     }
  511.     }
  512. }
  513.  
  514. /* Okay, now we write the entry_function for variable completion. */
  515. char *
  516. variable_completion_function (text, state)
  517.      int state;
  518.      char *text;
  519. {
  520.   static char *varname = (char *)NULL;
  521.   static SHELL_VAR *list;
  522.   static int namelen;
  523.  
  524.   if (!state)
  525.     {
  526.       if (varname)
  527.     free (varname);
  528.       varname = savestring (&text[1]);
  529.       namelen = strlen (varname);
  530.       list = variable_list;
  531.     }
  532.  
  533.   while (list)
  534.     {
  535.       /* Compare.  You can't do better than Zayre.  No text is also
  536.      a match.  */
  537.       if (!invisible_p (list) &&
  538.       (!*varname || (strncmp (varname, list->name, namelen) == 0)))
  539.     break;
  540.       list = list->next;
  541.     }
  542.  
  543.   if (!list)
  544.     {
  545.       /* Then we are done. */
  546.       return ((char *)NULL);
  547.     }
  548.   else
  549.     {
  550.       char *value = (char *)xmalloc (2 + strlen (list->name));
  551.       *value = '$';
  552.       strcpy (&value[1], list->name);
  553.       list = list->next;
  554.       return (value);
  555.     }
  556. }
  557.  
  558. /* How about a completion function for hostnames? */
  559. char *
  560. hostname_completion_function (text, state)
  561.      int state;
  562.      char *text;
  563. {
  564.   static char **list = (char **)NULL;
  565.   static int list_index = 0;
  566.  
  567.   /* If we don't have any state, make some. */
  568.   if (!state)
  569.     {
  570.       extern char **hostnames_matching ();
  571.  
  572.       if (list)
  573.     free (list);
  574.       list = (char **)NULL;
  575.  
  576.       list = hostnames_matching (*text ? &text[1] : &text[0]);
  577.       list_index = 0;
  578.     }
  579.  
  580.   if (list && list[list_index])
  581.     {
  582.       char *t = (char *)xmalloc (2 + strlen (list[list_index]));
  583.  
  584.       *t = *text;
  585.       strcpy (t + 1, list[list_index]);
  586.       list_index++;
  587.       return (t);
  588.     }
  589.   else
  590.     return ((char *)NULL);
  591. }
  592.  
  593. /* History and alias expand the line.  But maybe do more?  This
  594.    is a test to see what users like.  Do expand_string on the string. */
  595. shell_expand_line (ignore)
  596.      int ignore;
  597. {
  598.   char *pre_process_line (), *new_line;
  599.  
  600.   new_line = pre_process_line (rl_line_buffer, 0, 0);
  601.  
  602.   if (new_line)
  603.     {
  604.       int old_point = rl_point;
  605.       int at_end = rl_point == rl_end;
  606.  
  607.       /* If the line was history and alias expanded, then make that
  608.      be one thing to undo. */
  609.  
  610.       if (strcmp (new_line, rl_line_buffer) != 0)
  611.     {
  612.       rl_point = rl_end;
  613.  
  614.       rl_add_undo (UNDO_BEGIN, 0, 0, 0);
  615.       rl_kill_text (0, rl_point);
  616.       rl_point = rl_end = 0;
  617.       rl_insert_text (new_line);
  618.       rl_add_undo (UNDO_END, 0, 0, 0);
  619.     }
  620.  
  621.       free (new_line);
  622.  
  623.       /* If there is variable expansion to perform, do that as a separate
  624.      operation to be undone. */
  625.       {
  626.     char *expand_string (), *expanded_string;
  627.     char *string_list ();
  628.  
  629.     expanded_string = expand_string (rl_line_buffer, 0);
  630.     if (!expanded_string)
  631.       new_line = savestring ("");
  632.     else
  633.       {
  634.         new_line = string_list (expanded_string);
  635.         dispose_words (expanded_string);
  636.       }
  637.  
  638.       if (strcmp (new_line, rl_line_buffer) != 0)
  639.     {
  640.       rl_add_undo (UNDO_BEGIN, 0, 0 ,0);
  641.       rl_kill_text (0, rl_end);
  642.       rl_point = rl_end = 0;
  643.       rl_insert_text (new_line);
  644.       rl_add_undo (UNDO_END, 0, 0, 0);
  645.     }
  646.  
  647.       free (new_line);
  648.  
  649.       /* Place rl_point where we think it should go. */
  650.       if (at_end)
  651.     rl_point = rl_end;
  652.       else if (old_point < rl_end)
  653.     {
  654.       rl_point = old_point;
  655.       if (!whitespace (rl_line_buffer[rl_point]))
  656.         rl_forward_word (1);
  657.     }
  658.       }
  659.     }
  660.   else
  661.     {
  662.       /* There was an error in expansion.  Let the preprocessor print
  663.      the error here.  Note that we know that pre_process_line ()
  664.      will return NULL, since it just did. */
  665.       fprintf (rl_outstream, "\n\r");
  666.       pre_process_line (rl_line_buffer, 1, 0);
  667.       rl_forced_update_display ();
  668.     }
  669. }
  670.  
  671. /* If tilde_expand hasn't been able to expand the text, perhaps it
  672.    is a special shell expansion.  We handle that here. */
  673. char *
  674. bash_tilde_expand (text)
  675.      char *text;
  676. {
  677.   char *result = (char *)NULL;
  678.  
  679.   if (strcmp (text, "-") == 0)
  680.     result = get_string_value ("OLDPWD");
  681.   else if (strcmp (text, "+") == 0)
  682.     result = get_string_value ("PWD");
  683.  
  684.   if (result)
  685.     result = savestring (result);
  686.  
  687.   return (result);
  688. }
  689.  
  690. /* Handle symbolic link references while hacking completion. */
  691. bash_symbolic_link_hook (dirname)
  692.      char **dirname;
  693. {
  694.   extern int follow_symbolic_links;
  695.   char *make_absolute (), *temp_dirname;
  696.  
  697.   if (follow_symbolic_links && (strcmp (*dirname, ".") != 0))
  698.     {
  699.       temp_dirname = make_absolute (*dirname, get_working_directory (""));
  700.  
  701.       if (temp_dirname)
  702.     {
  703.       free (*dirname);
  704.       *dirname = temp_dirname;
  705.     }
  706.     }
  707. }
  708.