Patch-ID: bash41-006
[bash.git] / CWRU / mh-folder-comp
blob905000c7a34cb5fe343f09a73c5b3d99a5be55ab
1 From jwe@che.utexas.edu Wed Sep 21 17:23:40 1994
2 Flags: 10
3 Return-Path: jwe@che.utexas.edu
4 Received: from po.CWRU.Edu (root@po.CWRU.Edu [129.22.4.2]) by odin.INS.CWRU.Edu with ESMTP (8.6.8.1+cwru/CWRU-2.1-ins)
5         id RAA04010; Wed, 21 Sep 1994 17:23:39 -0400 (from jwe@che.utexas.edu for <chet@odin.INS.CWRU.Edu>)
6 Received: from life.ai.mit.edu (life.ai.mit.edu [128.52.32.80]) by po.CWRU.Edu with SMTP (8.6.8.1+cwru/CWRU-2.2)
7         id RAA02121; Wed, 21 Sep 1994 17:23:28 -0400 (from jwe@che.utexas.edu for <chet@po.cwru.edu>)
8 Received: from schoch.che.utexas.edu by life.ai.mit.edu (4.1/AI-4.10) for chet@po.cwru.edu id AA09989; Wed, 21 Sep 94 17:23:17 EDT
9 Received: from localhost (jwe@localhost) by schoch.che.utexas.edu (8.6.8.1/8.6) with SMTP id QAA05737; Wed, 21 Sep 1994 16:22:01 -0500
10 Message-Id: <199409212122.QAA05737@schoch.che.utexas.edu>
11 To: march@tudor.com
12 Cc: bug-bash@prep.ai.mit.edu
13 Subject: Re: Completion feature possible?
14 In-Reply-To: Your message of 21 Sep 94 13:30:22 EDT
15 Date: Wed, 21 Sep 94 16:22:00 EDT
16 From: John Eaton <jwe@che.utexas.edu>
18 Gregory F. March <march@tudor.com> wrote:
20 : I was having a discussion about MH with one of my friends the other
21 : day and I got to thinking that the +folder/subfolder scheme for naming
22 : mail folders is a real pain because completion doesn't work on
23 : them. Someone then mentioned that zsh (I think) has the ability to
24 : specify how to complete (I guess where to look for the files) for
25 : different prefixes. Bash right now knows about '@', '~', and '$' (any
26 : others?). It would be really helpful if one could define something
27 : like:
28
29 :       completion '+' "$HOME/Mail"
30
31 : in a config file someplace. Would this be easy? Is there a list of
32 : TODO item that someone might want to add this to?
34 It would be nice to have a general completion feature like this.
36 Until that happens, maybe you will find the following patch useful.
37 It makes MH folder name completion work with bash.  The diffs are
38 relative to version 1.14.2.
40 I realize that changes to readline.c and and complete.c are not good
41 since they add some MH-specific stuff to the readline code and not to
42 bash, but when I first wrote this, I had no idea what else to do.
44 Chet, would you consider adding this if it were cleaned up a bit?
45 Made optional with cpp conditionals?
47 This feature has been very useful to me for the last several years
48 (since about 1.05 or 1.06, I think).
50 Thanks,
53 John W. Eaton      | 4.3BSD is not perfect.  -- Leffler, et al. (1989).
54 jwe@che.utexas.edu |
57 -------------------------------cut here-------------------------------
58 diff -rc bash-1.14.2/bashline.c bash-1.14.2.local/bashline.c
59 *** bash-1.14.2/bashline.c      Wed Aug  3 09:32:45 1994
60 --- bash-1.14.2.local/bashline.c        Wed Sep 21 15:39:04 1994
61 ***************
62 *** 58,63 ****
63 --- 58,64 ----
64   static char *hostname_completion_function ();
65   static char *command_word_completion_function ();
66   static char *command_subst_completion_function ();
67 + static char *mh_folder_completion_function ();
68   
69   static void snarf_hosts_from_file (), add_host_name ();
70   static void sort_hostname_list ();
71 ***************
72 *** 90,95 ****
73 --- 91,98 ----
74     bash_complete_username_internal (),
75     bash_complete_hostname (), bash_possible_hostname_completions (),
76     bash_complete_hostname_internal (),
77 +   bash_complete_mh_folder (), bash_possible_mh_folder_completions (),
78 +   bash_complete_mh_folder_internal (),
79     bash_complete_variable (), bash_possible_variable_completions (),
80     bash_complete_variable_internal (),
81     bash_complete_command (), bash_possible_command_completions (),
82 ***************
83 *** 134,140 ****
84     rl_terminal_name = get_string_value ("TERM");
85     rl_instream = stdin;
86     rl_outstream = stderr;
87 !   rl_special_prefixes = "$@";
88   
89     /* Allow conditional parsing of the ~/.inputrc file. */
90     rl_readline_name = "Bash";
91 --- 137,143 ----
92     rl_terminal_name = get_string_value ("TERM");
93     rl_instream = stdin;
94     rl_outstream = stderr;
95 !   rl_special_prefixes = "$@+";
96   
97     /* Allow conditional parsing of the ~/.inputrc file. */
98     rl_readline_name = "Bash";
99 ***************
100 *** 193,198 ****
101 --- 196,207 ----
102     rl_bind_key_in_map ('@', bash_possible_hostname_completions,
103                       emacs_ctlx_keymap);
104   
105 +   rl_add_defun ("complete-mh-folder", bash_complete_mh_folder, META('+'));
106 +   rl_add_defun ("possible-mh-folder-completions",
107 +               bash_possible_mh_folder_completions, -1);
108 +   rl_bind_key_in_map ('+', bash_possible_mh_folder_completions,
109 +                     emacs_ctlx_keymap);
111     rl_add_defun ("complete-variable", bash_complete_variable, -1);
112     rl_bind_key_in_map ('$', bash_complete_variable, emacs_meta_keymap);
113     rl_add_defun ("possible-variable-completions",
114 ***************
115 *** 656,661 ****
116 --- 665,677 ----
117     if (!matches && *text == '@')
118       matches = completion_matches (text, hostname_completion_function);
119   
120 +   /* Another one.  Why not?  If the word starts in '+', then look for
121 +      matching mh folders for completion first. */
122 +   if (!matches && *text == '+')
123 +     {
124 +       matches = completion_matches (text, mh_folder_completion_function);
125 +     }
127     /* And last, (but not least) if this word is in a command position, then
128        complete over possible command names, including aliases, functions,
129        and command names. */
130 ***************
131 *** 1077,1082 ****
132 --- 1093,1185 ----
133       return ((char *)NULL);
134   }
135   
136 + /* How about a completion function for mh folders? */
137 + static char *
138 + mh_folder_completion_function (text, state)
139 +      int state;
140 +      char *text;
141 + {
142 +   extern int rl_filename_completion_desired;
144 +   extern char *get_mh_path ();
146 +   static char *mh_path = (char *)NULL;
147 +   static int len;
148 +   static int istate;
149 +   static char *val;
150 +   char *hint;
152 +   static char *mh_folder_hint = (char *)NULL;
154 +   /* If we don't have any state, make some. */
155 +   if (!state)
156 +     {
157 +       val = (char *)NULL;
159 +       if (mh_path)
160 +       free (mh_path);
162 +       mh_path = get_mh_path ();
163 +       if (!mh_path && !(hint[1] == '/' || hint[1] == '.'))
164 +       return ((char *)NULL);
166 +       len = strlen (mh_path);
167 +     }
169 +   if (mh_folder_hint)
170 +     free (mh_folder_hint);
172 +   hint = text;
173 +   if (*hint == '+')
174 +     hint++;
176 +   mh_folder_hint = (char *)xmalloc (2 + len + strlen (hint));
177 +   if (*hint == '/' || *hint == '.') {
178 +     len = -1;
179 +     sprintf (mh_folder_hint, "%s", hint);
180 +   } else
181 +     sprintf (mh_folder_hint, "%s/%s", mh_path, hint);
183 +   istate = (val != (char *)NULL);
185 +  again:
186 +   val = filename_completion_function (mh_folder_hint, istate);
187 +   istate = 1;
189 +   if (!val)
190 +     {
191 +       return ((char *)NULL);
192 +     }
193 +   else
194 +     {
195 +       char *ptr = val + len + 1, *temp;
196 +       struct stat sb;
197 +       int status = stat (val, &sb);
199 +       if (status != 0)
200 +       return ((char *)NULL);
202 +       if ((sb.st_mode & S_IFDIR) == S_IFDIR)
203 +       {
204 +         temp = (char *)xmalloc (2 + strlen (ptr));
205 +         *temp = '+';
206 +         strcpy (temp + 1, ptr);
208 +         free (val);
209 +         val = "";
211 +         rl_filename_completion_desired = 1;
213 +         return (temp);
214 +       }
215 +       else
216 +       {
217 +         free (val);
218 +       }
219 +       goto again;
220 +     }
221 + }
223   /* History and alias expand the line. */
224   static char *
225   history_expand_line_internal (line)
226 ***************
227 *** 1628,1633 ****
228 --- 1731,1773 ----
229   {
230     bash_specific_completion
231       (what_to_do, (Function *)username_completion_function);
232 + }
234 + static void
235 + bash_complete_mh_folder (ignore, ignore2)
236 +      int ignore, ignore2;
237 + {
238 +   bash_complete_mh_folder_internal (TAB);
239 + }
241 + static void
242 + bash_possible_mh_folder_completions (ignore, ignore2)
243 +      int ignore, ignore2;
244 + {
245 +   bash_complete_mh_folder_internal ('?');
246 + }
248 + static void
249 + bash_complete_mh_folder_internal (what_to_do)
250 +      int what_to_do;
251 + {
252 +   Function  *orig_func;
253 +   CPPFunction *orig_attempt_func;
254 +   char *orig_rl_completer_word_break_characters;
255 +   extern char *rl_completer_word_break_characters;
257 +   orig_func = rl_completion_entry_function;
258 +   orig_attempt_func = rl_attempted_completion_function;
259 +   orig_rl_completer_word_break_characters = rl_completer_word_break_characters;
260 +   rl_completion_entry_function = (Function *)mh_folder_completion_function;
261 +   rl_attempted_completion_function = (CPPFunction *)NULL;
262 +   rl_completer_word_break_characters = " \t\n\"\'";
264 +   rl_complete_internal (what_to_do);
266 +   rl_completion_entry_function = orig_func;
267 +   rl_attempted_completion_function = orig_attempt_func;
268 +   rl_completer_word_break_characters = orig_rl_completer_word_break_characters;
269   }
270   
271   static void
272 Only in bash-1.14.2.local: bashline.c.orig
273 diff -rc bash-1.14.2/lib/readline/complete.c bash-1.14.2.local/lib/readline/complete.c
274 *** bash-1.14.2/lib/readline/complete.c Tue Jul 26 12:59:57 1994
275 --- bash-1.14.2.local/lib/readline/complete.c   Wed Sep 21 15:41:19 1994
276 ***************
277 *** 733,751 ****
278               if (rl_filename_completion_desired)
279                 {
280                   struct stat finfo;
281 !                 char *filename = tilde_expand (matches[0]);
282   
283 !                 if ((stat (filename, &finfo) == 0) && S_ISDIR (finfo.st_mode))
284                     {
285 !                     if (rl_line_buffer[rl_point] != '/')
286 !                       rl_insert_text ("/");
287                     }
288 !                 else
289                     {
290 !                     if (rl_point == rl_end)
291 !                       rl_insert_text (temp_string);
292                     }
293 -                 free (filename);
294                 }
295               else
296                 {
297 --- 733,768 ----
298               if (rl_filename_completion_desired)
299                 {
300                   struct stat finfo;
301 !                 char *tilde_expand ();
302 !                 char *plus_expand ();
303 !                 char *filename = (char *) NULL;
304   
305 !                 switch (*matches[0])
306                     {
307 !                   case '+':
308 !                     filename = plus_expand (matches[0]);
309 !                     break;
310 !                   case '~':
311 !                   default:
312 !                     filename = tilde_expand (matches[0]);
313 !                     break;
314                     }
316 !                 if (filename)
317                     {
318 !                     if ((stat (filename, &finfo) == 0)
319 !                         && S_ISDIR (finfo.st_mode))
320 !                       {
321 !                         if (rl_line_buffer[rl_point] != '/')
322 !                           rl_insert_text ("/");
323 !                       }
324 !                     else
325 !                       {
326 !                         if (rl_point == rl_end)
327 !                           rl_insert_text (temp_string);
328 !                       }
329 !                     free (filename);
330                     }
331                 }
332               else
333                 {
334 Only in bash-1.14.2.local/lib/readline: diffs
335 diff -rc bash-1.14.2/lib/readline/readline.c bash-1.14.2.local/lib/readline/readline.c
336 *** bash-1.14.2/lib/readline/readline.c Fri Aug 12 12:47:46 1994
337 --- bash-1.14.2.local/lib/readline/readline.c   Wed Sep 21 15:36:07 1994
338 ***************
339 *** 23,28 ****
340 --- 23,29 ----
341   #define READLINE_LIBRARY
342   
343   #include <stdio.h>
344 + #include <string.h>
345   #include <sys/types.h>
346   #include <fcntl.h>
347   #if !defined (NO_SYS_FILE)
348 ***************
349 *** 3518,3523 ****
350 --- 3519,3616 ----
351   }
352   
353   #endif /* TEST */
355 + #define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
357 + char *
358 + get_mh_path ()
359 + {
360 +   static FILE *fp = (FILE *)NULL;
361 +   char buf[512];      /* XXX */
362 +   char profile[512];  /* XXX */
363 +   char *bp;
364 +   char *temp_home;
365 +   char *temp_path;
367 +   temp_home = (char *)getenv ("HOME");
368 +   if (!temp_home)
369 +     return ((char *)NULL);
371 +   strcpy (profile, temp_home);
372 +   strcat (profile, "/.mh_profile");
374 +   if (fp)
375 +     fclose (fp);
377 +   fp = fopen (profile, "r");
378 +   if (fp == (FILE *)NULL)
379 +     return ((char *)NULL);
381 +   while (fgets (buf, 512, fp) != (char *)NULL)  /* XXX */
382 +     {
383 +       if ((bp = strstr (buf, "Path:")) != (char *)NULL)
384 +       {
385 +         bp += 5;
386 +         while (whitespace (*bp))
387 +           bp++;
389 +         if (*bp == '\0')
390 +           return ((char *)NULL);
392 +         temp_path = (char *)xmalloc (3 + strlen (bp) + strlen (temp_home));
394 +         strcpy (temp_path, temp_home);
395 +         strcat (temp_path, "/");
396 +         strcat (temp_path, bp);
398 +         bp = temp_path;
400 +         while (!(cr_whitespace (*bp)))
401 +           bp++;
403 +         *bp = '\0';
405 +         return temp_path;
406 +       }
407 +     }
409 +   return ((char *)NULL);
410 + }
412 + /* Expand FILENAME if it begins with a plus.  This always returns
413 +    a new string. */
414 + char *
415 + plus_expand (filename)
416 +      char *filename;
417 + {
418 +   static char *dirname = (char *)NULL;
420 +   if (filename && *filename == '+')
421 +     {
422 +       char *mh_path = get_mh_path ();
424 +       if (filename[1] == '/' || filename[1] == '.')
425 +       {
426 +         dirname = (char *)xmalloc (1 + strlen (filename));
428 +         strcpy(dirname, filename+1);
430 +         return dirname;
431 +       }
433 +       if (mh_path)
434 +       {
435 +         dirname = (char *)xmalloc (1 + strlen (filename) + strlen (mh_path));
437 +         strcpy (dirname, mh_path);
438 +         strcat (dirname, "/");
439 +         strcat (dirname, filename+1);
441 +         return dirname;
442 +       }
443 +     }
444 +   return (char *)NULL;
445 + }
446   
447   \f
448   /*