Imported from ../bash-2.0.tar.gz.
[platform/upstream/bash.git] / CWRU / mh-folder-comp
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>
17
18 Gregory F. March <march@tudor.com> wrote:
19
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?
33
34 It would be nice to have a general completion feature like this.
35
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.
39
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.
43
44 Chet, would you consider adding this if it were cleaned up a bit?
45 Made optional with cpp conditionals?
46
47 This feature has been very useful to me for the last several years
48 (since about 1.05 or 1.06, I think).
49
50 Thanks,
51
52 --
53 John W. Eaton      | 4.3BSD is not perfect.  -- Leffler, et al. (1989).
54 jwe@che.utexas.edu |
55
56
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);
110
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 +     }
126
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;
143
144 +   extern char *get_mh_path ();
145
146 +   static char *mh_path = (char *)NULL;
147 +   static int len;
148 +   static int istate;
149 +   static char *val;
150 +   char *hint;
151
152 +   static char *mh_folder_hint = (char *)NULL;
153
154 +   /* If we don't have any state, make some. */
155 +   if (!state)
156 +     {
157 +       val = (char *)NULL;
158
159 +       if (mh_path)
160 +       free (mh_path);
161
162 +       mh_path = get_mh_path ();
163 +       if (!mh_path && !(hint[1] == '/' || hint[1] == '.'))
164 +       return ((char *)NULL);
165
166 +       len = strlen (mh_path);
167 +     }
168
169 +   if (mh_folder_hint)
170 +     free (mh_folder_hint);
171
172 +   hint = text;
173 +   if (*hint == '+')
174 +     hint++;
175
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);
182
183 +   istate = (val != (char *)NULL);
184
185 +  again:
186 +   val = filename_completion_function (mh_folder_hint, istate);
187 +   istate = 1;
188
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);
198
199 +       if (status != 0)
200 +       return ((char *)NULL);
201
202 +       if ((sb.st_mode & S_IFDIR) == S_IFDIR)
203 +       {
204 +         temp = (char *)xmalloc (2 + strlen (ptr));
205 +         *temp = '+';
206 +         strcpy (temp + 1, ptr);
207
208 +         free (val);
209 +         val = "";
210
211 +         rl_filename_completion_desired = 1;
212
213 +         return (temp);
214 +       }
215 +       else
216 +       {
217 +         free (val);
218 +       }
219 +       goto again;
220 +     }
221 + }
222
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 + }
233
234 + static void
235 + bash_complete_mh_folder (ignore, ignore2)
236 +      int ignore, ignore2;
237 + {
238 +   bash_complete_mh_folder_internal (TAB);
239 + }
240
241 + static void
242 + bash_possible_mh_folder_completions (ignore, ignore2)
243 +      int ignore, ignore2;
244 + {
245 +   bash_complete_mh_folder_internal ('?');
246 + }
247
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;
256
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\"\'";
263
264 +   rl_complete_internal (what_to_do);
265
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                     }
315
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 */
354
355 + #define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
356
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;
366
367 +   temp_home = (char *)getenv ("HOME");
368 +   if (!temp_home)
369 +     return ((char *)NULL);
370
371 +   strcpy (profile, temp_home);
372 +   strcat (profile, "/.mh_profile");
373
374 +   if (fp)
375 +     fclose (fp);
376
377 +   fp = fopen (profile, "r");
378 +   if (fp == (FILE *)NULL)
379 +     return ((char *)NULL);
380
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++;
388
389 +         if (*bp == '\0')
390 +           return ((char *)NULL);
391
392 +         temp_path = (char *)xmalloc (3 + strlen (bp) + strlen (temp_home));
393
394 +         strcpy (temp_path, temp_home);
395 +         strcat (temp_path, "/");
396 +         strcat (temp_path, bp);
397
398 +         bp = temp_path;
399
400 +         while (!(cr_whitespace (*bp)))
401 +           bp++;
402
403 +         *bp = '\0';
404
405 +         return temp_path;
406 +       }
407 +     }
408
409 +   return ((char *)NULL);
410 + }
411
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;
419
420 +   if (filename && *filename == '+')
421 +     {
422 +       char *mh_path = get_mh_path ();
423
424 +       if (filename[1] == '/' || filename[1] == '.')
425 +       {
426 +         dirname = (char *)xmalloc (1 + strlen (filename));
427
428 +         strcpy(dirname, filename+1);
429
430 +         return dirname;
431 +       }
432
433 +       if (mh_path)
434 +       {
435 +         dirname = (char *)xmalloc (1 + strlen (filename) + strlen (mh_path));
436
437 +         strcpy (dirname, mh_path);
438 +         strcat (dirname, "/");
439 +         strcat (dirname, filename+1);
440
441 +         return dirname;
442 +       }
443 +     }
444 +   return (char *)NULL;
445 + }
446   
447   \f
448   /*
449