11acd27e7Smillert /* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */ 21acd27e7Smillert 31acd27e7Smillert /* Copyright (C) 1988,1989 Free Software Foundation, Inc. 41acd27e7Smillert 51acd27e7Smillert This file is part of GNU Readline, a library for reading lines 61acd27e7Smillert of text with interactive input and history editing. 71acd27e7Smillert 81acd27e7Smillert Readline is free software; you can redistribute it and/or modify it 91acd27e7Smillert under the terms of the GNU General Public License as published by the 101acd27e7Smillert Free Software Foundation; either version 2, or (at your option) any 111acd27e7Smillert later version. 121acd27e7Smillert 131acd27e7Smillert Readline is distributed in the hope that it will be useful, but 141acd27e7Smillert WITHOUT ANY WARRANTY; without even the implied warranty of 151acd27e7Smillert MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 161acd27e7Smillert General Public License for more details. 171acd27e7Smillert 181acd27e7Smillert You should have received a copy of the GNU General Public License 191acd27e7Smillert along with Readline; see the file COPYING. If not, write to the Free 201acd27e7Smillert Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ 211acd27e7Smillert 221acd27e7Smillert #if defined (HAVE_CONFIG_H) 231acd27e7Smillert # include <config.h> 241acd27e7Smillert #endif 251acd27e7Smillert 261acd27e7Smillert #if defined (HAVE_UNISTD_H) 271acd27e7Smillert # ifdef _MINIX 281acd27e7Smillert # include <sys/types.h> 291acd27e7Smillert # endif 301acd27e7Smillert # include <unistd.h> 311acd27e7Smillert #endif 321acd27e7Smillert 331acd27e7Smillert #if defined (HAVE_STRING_H) 341acd27e7Smillert # include <string.h> 351acd27e7Smillert #else /* !HAVE_STRING_H */ 361acd27e7Smillert # include <strings.h> 371acd27e7Smillert #endif /* !HAVE_STRING_H */ 381acd27e7Smillert 391acd27e7Smillert #if defined (HAVE_STDLIB_H) 401acd27e7Smillert # include <stdlib.h> 411acd27e7Smillert #else 421acd27e7Smillert # include "ansi_stdlib.h" 431acd27e7Smillert #endif /* HAVE_STDLIB_H */ 441acd27e7Smillert 451acd27e7Smillert #include <sys/types.h> 461acd27e7Smillert #include <pwd.h> 471acd27e7Smillert 481acd27e7Smillert #include "tilde.h" 491acd27e7Smillert 501acd27e7Smillert #if defined (TEST) || defined (STATIC_MALLOC) 511acd27e7Smillert static char *xmalloc (), *xrealloc (); 521acd27e7Smillert #else 531acd27e7Smillert # if defined __STDC__ 541acd27e7Smillert extern char *xmalloc (int); 551acd27e7Smillert extern char *xrealloc (void *, int); 561acd27e7Smillert # else 571acd27e7Smillert extern char *xmalloc (), *xrealloc (); 581acd27e7Smillert # endif /* !__STDC__ */ 591acd27e7Smillert #endif /* TEST || STATIC_MALLOC */ 601acd27e7Smillert 611acd27e7Smillert #if !defined (HAVE_GETPW_DECLS) 621acd27e7Smillert extern struct passwd *getpwuid (), *getpwnam (); 631acd27e7Smillert #endif /* !HAVE_GETPW_DECLS */ 641acd27e7Smillert 651acd27e7Smillert #if !defined (savestring) 661acd27e7Smillert # ifndef strcpy 671acd27e7Smillert extern char *strcpy (); 681acd27e7Smillert # endif 691acd27e7Smillert #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x)) 701acd27e7Smillert #endif /* !savestring */ 711acd27e7Smillert 721acd27e7Smillert #if !defined (NULL) 731acd27e7Smillert # if defined (__STDC__) 741acd27e7Smillert # define NULL ((void *) 0) 751acd27e7Smillert # else 761acd27e7Smillert # define NULL 0x0 771acd27e7Smillert # endif /* !__STDC__ */ 781acd27e7Smillert #endif /* !NULL */ 791acd27e7Smillert 801acd27e7Smillert /* If being compiled as part of bash, these will be satisfied from 811acd27e7Smillert variables.o. If being compiled as part of readline, they will 821acd27e7Smillert be satisfied from shell.o. */ 831acd27e7Smillert extern char *get_home_dir __P((void)); 841acd27e7Smillert extern char *get_env_value __P((char *)); 851acd27e7Smillert 861acd27e7Smillert /* The default value of tilde_additional_prefixes. This is set to 871acd27e7Smillert whitespace preceding a tilde so that simple programs which do not 881acd27e7Smillert perform any word separation get desired behaviour. */ 891acd27e7Smillert static char *default_prefixes[] = 901acd27e7Smillert { " ~", "\t~", (char *)NULL }; 911acd27e7Smillert 921acd27e7Smillert /* The default value of tilde_additional_suffixes. This is set to 931acd27e7Smillert whitespace or newline so that simple programs which do not 941acd27e7Smillert perform any word separation get desired behaviour. */ 951acd27e7Smillert static char *default_suffixes[] = 961acd27e7Smillert { " ", "\n", (char *)NULL }; 971acd27e7Smillert 981acd27e7Smillert /* If non-null, this contains the address of a function that the application 991acd27e7Smillert wants called before trying the standard tilde expansions. The function 1001acd27e7Smillert is called with the text sans tilde, and returns a malloc()'ed string 1011acd27e7Smillert which is the expansion, or a NULL pointer if the expansion fails. */ 1021acd27e7Smillert CPFunction *tilde_expansion_preexpansion_hook = (CPFunction *)NULL; 1031acd27e7Smillert 1041acd27e7Smillert /* If non-null, this contains the address of a function to call if the 1051acd27e7Smillert standard meaning for expanding a tilde fails. The function is called 1061acd27e7Smillert with the text (sans tilde, as in "foo"), and returns a malloc()'ed string 1071acd27e7Smillert which is the expansion, or a NULL pointer if there is no expansion. */ 1081acd27e7Smillert CPFunction *tilde_expansion_failure_hook = (CPFunction *)NULL; 1091acd27e7Smillert 1101acd27e7Smillert /* When non-null, this is a NULL terminated array of strings which 1111acd27e7Smillert are duplicates for a tilde prefix. Bash uses this to expand 1121acd27e7Smillert `=~' and `:~'. */ 1131acd27e7Smillert char **tilde_additional_prefixes = default_prefixes; 1141acd27e7Smillert 1151acd27e7Smillert /* When non-null, this is a NULL terminated array of strings which match 1161acd27e7Smillert the end of a username, instead of just "/". Bash sets this to 1171acd27e7Smillert `:' and `=~'. */ 1181acd27e7Smillert char **tilde_additional_suffixes = default_suffixes; 1191acd27e7Smillert 1201acd27e7Smillert /* Find the start of a tilde expansion in STRING, and return the index of 1211acd27e7Smillert the tilde which starts the expansion. Place the length of the text 1221acd27e7Smillert which identified this tilde starter in LEN, excluding the tilde itself. */ 1231acd27e7Smillert static int 1241acd27e7Smillert tilde_find_prefix (string, len) 1251acd27e7Smillert char *string; 1261acd27e7Smillert int *len; 1271acd27e7Smillert { 1281acd27e7Smillert register int i, j, string_len; 1291acd27e7Smillert register char **prefixes; 1301acd27e7Smillert 1311acd27e7Smillert prefixes = tilde_additional_prefixes; 1321acd27e7Smillert 1331acd27e7Smillert string_len = strlen (string); 1341acd27e7Smillert *len = 0; 1351acd27e7Smillert 1361acd27e7Smillert if (*string == '\0' || *string == '~') 1371acd27e7Smillert return (0); 1381acd27e7Smillert 1391acd27e7Smillert if (prefixes) 1401acd27e7Smillert { 1411acd27e7Smillert for (i = 0; i < string_len; i++) 1421acd27e7Smillert { 1431acd27e7Smillert for (j = 0; prefixes[j]; j++) 1441acd27e7Smillert { 1451acd27e7Smillert if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0) 1461acd27e7Smillert { 1471acd27e7Smillert *len = strlen (prefixes[j]) - 1; 1481acd27e7Smillert return (i + *len); 1491acd27e7Smillert } 1501acd27e7Smillert } 1511acd27e7Smillert } 1521acd27e7Smillert } 1531acd27e7Smillert return (string_len); 1541acd27e7Smillert } 1551acd27e7Smillert 1561acd27e7Smillert /* Find the end of a tilde expansion in STRING, and return the index of 1571acd27e7Smillert the character which ends the tilde definition. */ 1581acd27e7Smillert static int 1591acd27e7Smillert tilde_find_suffix (string) 1601acd27e7Smillert char *string; 1611acd27e7Smillert { 1621acd27e7Smillert register int i, j, string_len; 1631acd27e7Smillert register char **suffixes; 1641acd27e7Smillert 1651acd27e7Smillert suffixes = tilde_additional_suffixes; 1661acd27e7Smillert string_len = strlen (string); 1671acd27e7Smillert 1681acd27e7Smillert for (i = 0; i < string_len; i++) 1691acd27e7Smillert { 1701acd27e7Smillert #if defined (__MSDOS__) 1711acd27e7Smillert if (string[i] == '/' || string[i] == '\\' /* || !string[i] */) 1721acd27e7Smillert #else 1731acd27e7Smillert if (string[i] == '/' /* || !string[i] */) 1741acd27e7Smillert #endif 1751acd27e7Smillert break; 1761acd27e7Smillert 1771acd27e7Smillert for (j = 0; suffixes && suffixes[j]; j++) 1781acd27e7Smillert { 1791acd27e7Smillert if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0) 1801acd27e7Smillert return (i); 1811acd27e7Smillert } 1821acd27e7Smillert } 1831acd27e7Smillert return (i); 1841acd27e7Smillert } 1851acd27e7Smillert 1861acd27e7Smillert /* Return a new string which is the result of tilde expanding STRING. */ 1871acd27e7Smillert char * 1881acd27e7Smillert tilde_expand (string) 1891acd27e7Smillert char *string; 1901acd27e7Smillert { 1911acd27e7Smillert char *result; 1921acd27e7Smillert int result_size, result_index; 1931acd27e7Smillert 1941acd27e7Smillert result_index = result_size = 0; 1951acd27e7Smillert if (result = strchr (string, '~')) 1961acd27e7Smillert result = xmalloc (result_size = (strlen (string) + 16)); 1971acd27e7Smillert else 1981acd27e7Smillert result = xmalloc (result_size = (strlen (string) + 1)); 1991acd27e7Smillert 2001acd27e7Smillert /* Scan through STRING expanding tildes as we come to them. */ 2011acd27e7Smillert while (1) 2021acd27e7Smillert { 2031acd27e7Smillert register int start, end; 2041acd27e7Smillert char *tilde_word, *expansion; 2051acd27e7Smillert int len; 2061acd27e7Smillert 2071acd27e7Smillert /* Make START point to the tilde which starts the expansion. */ 2081acd27e7Smillert start = tilde_find_prefix (string, &len); 2091acd27e7Smillert 2101acd27e7Smillert /* Copy the skipped text into the result. */ 2111acd27e7Smillert if ((result_index + start + 1) > result_size) 2121acd27e7Smillert result = xrealloc (result, 1 + (result_size += (start + 20))); 2131acd27e7Smillert 2141acd27e7Smillert strncpy (result + result_index, string, start); 2151acd27e7Smillert result_index += start; 2161acd27e7Smillert 2171acd27e7Smillert /* Advance STRING to the starting tilde. */ 2181acd27e7Smillert string += start; 2191acd27e7Smillert 2201acd27e7Smillert /* Make END be the index of one after the last character of the 2211acd27e7Smillert username. */ 2221acd27e7Smillert end = tilde_find_suffix (string); 2231acd27e7Smillert 2241acd27e7Smillert /* If both START and END are zero, we are all done. */ 2251acd27e7Smillert if (!start && !end) 2261acd27e7Smillert break; 2271acd27e7Smillert 2281acd27e7Smillert /* Expand the entire tilde word, and copy it into RESULT. */ 2291acd27e7Smillert tilde_word = xmalloc (1 + end); 2301acd27e7Smillert strncpy (tilde_word, string, end); 2311acd27e7Smillert tilde_word[end] = '\0'; 2321acd27e7Smillert string += end; 2331acd27e7Smillert 2341acd27e7Smillert expansion = tilde_expand_word (tilde_word); 2351acd27e7Smillert free (tilde_word); 2361acd27e7Smillert 2371acd27e7Smillert len = strlen (expansion); 2381acd27e7Smillert #ifdef __CYGWIN32__ 2391acd27e7Smillert /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when 2401acd27e7Smillert $HOME for `user' is /. On cygwin, // denotes a network drive. */ 2411acd27e7Smillert if (len > 1 || *expansion != '/' || *string != '/') 2421acd27e7Smillert #endif 2431acd27e7Smillert { 2441acd27e7Smillert if ((result_index + len + 1) > result_size) 2451acd27e7Smillert result = xrealloc (result, 1 + (result_size += (len + 20))); 2461acd27e7Smillert 2471acd27e7Smillert strcpy (result + result_index, expansion); 2481acd27e7Smillert result_index += len; 2491acd27e7Smillert } 2501acd27e7Smillert free (expansion); 2511acd27e7Smillert } 2521acd27e7Smillert 2531acd27e7Smillert result[result_index] = '\0'; 2541acd27e7Smillert 2551acd27e7Smillert return (result); 2561acd27e7Smillert } 2571acd27e7Smillert 2581acd27e7Smillert /* Take FNAME and return the tilde prefix we want expanded. If LENP is 2591acd27e7Smillert non-null, the index of the end of the prefix into FNAME is returned in 2601acd27e7Smillert the location it points to. */ 2611acd27e7Smillert static char * 2621acd27e7Smillert isolate_tilde_prefix (fname, lenp) 2631acd27e7Smillert char *fname; 2641acd27e7Smillert int *lenp; 2651acd27e7Smillert { 2661acd27e7Smillert char *ret; 2671acd27e7Smillert int i; 2681acd27e7Smillert 2691acd27e7Smillert ret = xmalloc (strlen (fname)); 2701acd27e7Smillert #if defined (__MSDOS__) 2711acd27e7Smillert for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++) 2721acd27e7Smillert #else 2731acd27e7Smillert for (i = 1; fname[i] && fname[i] != '/'; i++) 2741acd27e7Smillert #endif 2751acd27e7Smillert ret[i - 1] = fname[i]; 2761acd27e7Smillert ret[i - 1] = '\0'; 2771acd27e7Smillert if (lenp) 2781acd27e7Smillert *lenp = i; 2791acd27e7Smillert return ret; 2801acd27e7Smillert } 2811acd27e7Smillert 2821acd27e7Smillert /* Return a string that is PREFIX concatenated with SUFFIX starting at 2831acd27e7Smillert SUFFIND. */ 2841acd27e7Smillert static char * 2851acd27e7Smillert glue_prefix_and_suffix (prefix, suffix, suffind) 2861acd27e7Smillert char *prefix, *suffix; 2871acd27e7Smillert int suffind; 2881acd27e7Smillert { 2891acd27e7Smillert char *ret; 2901acd27e7Smillert int plen, slen; 2911acd27e7Smillert 2921acd27e7Smillert plen = (prefix && *prefix) ? strlen (prefix) : 0; 2931acd27e7Smillert slen = strlen (suffix + suffind); 2941acd27e7Smillert ret = xmalloc (plen + slen + 1); 2951acd27e7Smillert if (plen) 2961acd27e7Smillert strcpy (ret, prefix); 2971acd27e7Smillert strcpy (ret + plen, suffix + suffind); 2981acd27e7Smillert return ret; 2991acd27e7Smillert } 3001acd27e7Smillert 3011acd27e7Smillert /* Do the work of tilde expansion on FILENAME. FILENAME starts with a 3021acd27e7Smillert tilde. If there is no expansion, call tilde_expansion_failure_hook. 3031acd27e7Smillert This always returns a newly-allocated string, never static storage. */ 3041acd27e7Smillert char * 3051acd27e7Smillert tilde_expand_word (filename) 3061acd27e7Smillert char *filename; 3071acd27e7Smillert { 3081acd27e7Smillert char *dirname, *expansion, *username; 3091acd27e7Smillert int user_len; 3101acd27e7Smillert struct passwd *user_entry; 3111acd27e7Smillert 3121acd27e7Smillert if (filename == 0) 3131acd27e7Smillert return ((char *)NULL); 3141acd27e7Smillert 3151acd27e7Smillert if (*filename != '~') 3161acd27e7Smillert return (savestring (filename)); 3171acd27e7Smillert 3181acd27e7Smillert /* A leading `~/' or a bare `~' is *always* translated to the value of 3191acd27e7Smillert $HOME or the home directory of the current user, regardless of any 3201acd27e7Smillert preexpansion hook. */ 3211acd27e7Smillert if (filename[1] == '\0' || filename[1] == '/') 3221acd27e7Smillert { 3231acd27e7Smillert /* Prefix $HOME to the rest of the string. */ 3241acd27e7Smillert expansion = get_env_value ("HOME"); 3251acd27e7Smillert 3261acd27e7Smillert /* If there is no HOME variable, look up the directory in 3271acd27e7Smillert the password database. */ 328*94bc1d69Smillert if (expansion == 0 || *expansion == '\0') 3291acd27e7Smillert expansion = get_home_dir (); 3301acd27e7Smillert 3311acd27e7Smillert return (glue_prefix_and_suffix (expansion, filename, 1)); 3321acd27e7Smillert } 3331acd27e7Smillert 3341acd27e7Smillert username = isolate_tilde_prefix (filename, &user_len); 3351acd27e7Smillert 3361acd27e7Smillert if (tilde_expansion_preexpansion_hook) 3371acd27e7Smillert { 3381acd27e7Smillert expansion = (*tilde_expansion_preexpansion_hook) (username); 3391acd27e7Smillert if (expansion) 3401acd27e7Smillert { 3411acd27e7Smillert dirname = glue_prefix_and_suffix (expansion, filename, user_len); 3421acd27e7Smillert free (username); 3431acd27e7Smillert free (expansion); 3441acd27e7Smillert return (dirname); 3451acd27e7Smillert } 3461acd27e7Smillert } 3471acd27e7Smillert 3481acd27e7Smillert /* No preexpansion hook, or the preexpansion hook failed. Look in the 3491acd27e7Smillert password database. */ 3501acd27e7Smillert dirname = (char *)NULL; 3511acd27e7Smillert user_entry = getpwnam (username); 3521acd27e7Smillert if (user_entry == 0) 3531acd27e7Smillert { 3541acd27e7Smillert /* If the calling program has a special syntax for expanding tildes, 3551acd27e7Smillert and we couldn't find a standard expansion, then let them try. */ 3561acd27e7Smillert if (tilde_expansion_failure_hook) 3571acd27e7Smillert { 3581acd27e7Smillert expansion = (*tilde_expansion_failure_hook) (username); 3591acd27e7Smillert if (expansion) 3601acd27e7Smillert { 3611acd27e7Smillert dirname = glue_prefix_and_suffix (expansion, filename, user_len); 3621acd27e7Smillert free (expansion); 3631acd27e7Smillert } 3641acd27e7Smillert } 3651acd27e7Smillert free (username); 3661acd27e7Smillert /* If we don't have a failure hook, or if the failure hook did not 3671acd27e7Smillert expand the tilde, return a copy of what we were passed. */ 3681acd27e7Smillert if (dirname == 0) 3691acd27e7Smillert dirname = savestring (filename); 3701acd27e7Smillert } 3711acd27e7Smillert else 3721acd27e7Smillert { 3731acd27e7Smillert free (username); 3741acd27e7Smillert dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len); 3751acd27e7Smillert } 3761acd27e7Smillert 3771acd27e7Smillert endpwent (); 3781acd27e7Smillert return (dirname); 3791acd27e7Smillert } 3801acd27e7Smillert 3811acd27e7Smillert 3821acd27e7Smillert #if defined (TEST) 3831acd27e7Smillert #undef NULL 3841acd27e7Smillert #include <stdio.h> 3851acd27e7Smillert 3861acd27e7Smillert main (argc, argv) 3871acd27e7Smillert int argc; 3881acd27e7Smillert char **argv; 3891acd27e7Smillert { 3901acd27e7Smillert char *result, line[512]; 3911acd27e7Smillert int done = 0; 3921acd27e7Smillert 3931acd27e7Smillert while (!done) 3941acd27e7Smillert { 3951acd27e7Smillert printf ("~expand: "); 3961acd27e7Smillert fflush (stdout); 3971acd27e7Smillert 3981acd27e7Smillert if (!gets (line)) 3991acd27e7Smillert strcpy (line, "done"); 4001acd27e7Smillert 4011acd27e7Smillert if ((strcmp (line, "done") == 0) || 4021acd27e7Smillert (strcmp (line, "quit") == 0) || 4031acd27e7Smillert (strcmp (line, "exit") == 0)) 4041acd27e7Smillert { 4051acd27e7Smillert done = 1; 4061acd27e7Smillert break; 4071acd27e7Smillert } 4081acd27e7Smillert 4091acd27e7Smillert result = tilde_expand (line); 4101acd27e7Smillert printf (" --> %s\n", result); 4111acd27e7Smillert free (result); 4121acd27e7Smillert } 4131acd27e7Smillert exit (0); 4141acd27e7Smillert } 4151acd27e7Smillert 4161acd27e7Smillert static void memory_error_and_abort (); 4171acd27e7Smillert 4181acd27e7Smillert static char * 4191acd27e7Smillert xmalloc (bytes) 4201acd27e7Smillert int bytes; 4211acd27e7Smillert { 4221acd27e7Smillert char *temp = (char *)malloc (bytes); 4231acd27e7Smillert 4241acd27e7Smillert if (!temp) 4251acd27e7Smillert memory_error_and_abort (); 4261acd27e7Smillert return (temp); 4271acd27e7Smillert } 4281acd27e7Smillert 4291acd27e7Smillert static char * 4301acd27e7Smillert xrealloc (pointer, bytes) 4311acd27e7Smillert char *pointer; 4321acd27e7Smillert int bytes; 4331acd27e7Smillert { 4341acd27e7Smillert char *temp; 4351acd27e7Smillert 4361acd27e7Smillert if (!pointer) 4371acd27e7Smillert temp = (char *)malloc (bytes); 4381acd27e7Smillert else 4391acd27e7Smillert temp = (char *)realloc (pointer, bytes); 4401acd27e7Smillert 4411acd27e7Smillert if (!temp) 4421acd27e7Smillert memory_error_and_abort (); 4431acd27e7Smillert 4441acd27e7Smillert return (temp); 4451acd27e7Smillert } 4461acd27e7Smillert 4471acd27e7Smillert static void 4481acd27e7Smillert memory_error_and_abort () 4491acd27e7Smillert { 4501acd27e7Smillert fprintf (stderr, "readline: out of virtual memory\n"); 4511acd27e7Smillert abort (); 4521acd27e7Smillert } 4531acd27e7Smillert 4541acd27e7Smillert /* 4551acd27e7Smillert * Local variables: 4561acd27e7Smillert * compile-command: "gcc -g -DTEST -o tilde tilde.c" 4571acd27e7Smillert * end: 4581acd27e7Smillert */ 4591acd27e7Smillert #endif /* TEST */ 460