1From jwe@che.utexas.edu Wed Sep 21 17:23:40 1994 2Flags: 10 3Return-Path: jwe@che.utexas.edu 4Received: 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>) 6Received: 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>) 8Received: 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 9Received: 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 10Message-Id: <199409212122.QAA05737@schoch.che.utexas.edu> 11To: march@tudor.com 12Cc: bug-bash@prep.ai.mit.edu 13Subject: Re: Completion feature possible? 14In-Reply-To: Your message of 21 Sep 94 13:30:22 EDT 15Date: Wed, 21 Sep 94 16:22:00 EDT 16From: John Eaton <jwe@che.utexas.edu> 17 18Gregory 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 34It would be nice to have a general completion feature like this. 35 36Until that happens, maybe you will find the following patch useful. 37It makes MH folder name completion work with bash. The diffs are 38relative to version 1.14.2. 39 40I realize that changes to readline.c and and complete.c are not good 41since they add some MH-specific stuff to the readline code and not to 42bash, but when I first wrote this, I had no idea what else to do. 43 44Chet, would you consider adding this if it were cleaned up a bit? 45Made optional with cpp conditionals? 46 47This feature has been very useful to me for the last several years 48(since about 1.05 or 1.06, I think). 49 50Thanks, 51 52-- 53John W. Eaton | 4.3BSD is not perfect. -- Leffler, et al. (1989). 54jwe@che.utexas.edu | 55 56 57-------------------------------cut here------------------------------- 58diff -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 272Only in bash-1.14.2.local: bashline.c.orig 273diff -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 { 334Only in bash-1.14.2.local/lib/readline: diffs 335diff -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 448 /* 449 450