121308Sache/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */ 221308Sache 321308Sache/* Copyright (C) 1988,1989 Free Software Foundation, Inc. 421308Sache 521308Sache This file is part of GNU Readline, a library for reading lines 621308Sache of text with interactive input and history editing. 721308Sache 821308Sache Readline is free software; you can redistribute it and/or modify it 921308Sache under the terms of the GNU General Public License as published by the 1058310Sache Free Software Foundation; either version 2, or (at your option) any 1121308Sache later version. 1221308Sache 1321308Sache Readline is distributed in the hope that it will be useful, but 1421308Sache WITHOUT ANY WARRANTY; without even the implied warranty of 1521308Sache MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1621308Sache General Public License for more details. 1721308Sache 1821308Sache You should have received a copy of the GNU General Public License 1921308Sache along with Readline; see the file COPYING. If not, write to the Free 2058310Sache Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ 2121308Sache 2221308Sache#if defined (HAVE_CONFIG_H) 2321308Sache# include <config.h> 2421308Sache#endif 2521308Sache 2626497Sache#if defined (HAVE_UNISTD_H) 2735486Sache# ifdef _MINIX 2835486Sache# include <sys/types.h> 2935486Sache# endif 3026497Sache# include <unistd.h> 3126497Sache#endif 3226497Sache 3321308Sache#if defined (HAVE_STRING_H) 3421308Sache# include <string.h> 3521308Sache#else /* !HAVE_STRING_H */ 3621308Sache# include <strings.h> 3721308Sache#endif /* !HAVE_STRING_H */ 3821308Sache 3921308Sache#if defined (HAVE_STDLIB_H) 4021308Sache# include <stdlib.h> 4121308Sache#else 4221308Sache# include "ansi_stdlib.h" 4321308Sache#endif /* HAVE_STDLIB_H */ 4421308Sache 4521308Sache#include <sys/types.h> 46157184Sache#if defined (HAVE_PWD_H) 4721308Sache#include <pwd.h> 48157184Sache#endif 4921308Sache 5021308Sache#include "tilde.h" 5121308Sache 5258310Sache#if defined (TEST) || defined (STATIC_MALLOC) 53119610Sachestatic void *xmalloc (), *xrealloc (); 5458310Sache#else 55119610Sache# include "xmalloc.h" 5658310Sache#endif /* TEST || STATIC_MALLOC */ 5758310Sache 5821308Sache#if !defined (HAVE_GETPW_DECLS) 59157184Sache# if defined (HAVE_GETPWUID) 60119610Sacheextern struct passwd *getpwuid PARAMS((uid_t)); 61157184Sache# endif 62157184Sache# if defined (HAVE_GETPWNAM) 63119610Sacheextern struct passwd *getpwnam PARAMS((const char *)); 64157184Sache# endif 6521308Sache#endif /* !HAVE_GETPW_DECLS */ 6621308Sache 6721308Sache#if !defined (savestring) 68119610Sache#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x)) 6921308Sache#endif /* !savestring */ 7021308Sache 7121308Sache#if !defined (NULL) 7221308Sache# if defined (__STDC__) 7321308Sache# define NULL ((void *) 0) 7421308Sache# else 7521308Sache# define NULL 0x0 7621308Sache# endif /* !__STDC__ */ 7721308Sache#endif /* !NULL */ 7821308Sache 7947558Sache/* If being compiled as part of bash, these will be satisfied from 8047558Sache variables.o. If being compiled as part of readline, they will 8147558Sache be satisfied from shell.o. */ 82119610Sacheextern char *sh_get_home_dir PARAMS((void)); 83119610Sacheextern char *sh_get_env_value PARAMS((const char *)); 8447558Sache 8521308Sache/* The default value of tilde_additional_prefixes. This is set to 8621308Sache whitespace preceding a tilde so that simple programs which do not 8721308Sache perform any word separation get desired behaviour. */ 8875406Sachestatic const char *default_prefixes[] = 8975406Sache { " ~", "\t~", (const char *)NULL }; 9021308Sache 9121308Sache/* The default value of tilde_additional_suffixes. This is set to 9221308Sache whitespace or newline so that simple programs which do not 9321308Sache perform any word separation get desired behaviour. */ 9475406Sachestatic const char *default_suffixes[] = 9575406Sache { " ", "\n", (const char *)NULL }; 9621308Sache 9726497Sache/* If non-null, this contains the address of a function that the application 9826497Sache wants called before trying the standard tilde expansions. The function 9926497Sache is called with the text sans tilde, and returns a malloc()'ed string 10026497Sache which is the expansion, or a NULL pointer if the expansion fails. */ 10175406Sachetilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL; 10226497Sache 10321308Sache/* If non-null, this contains the address of a function to call if the 10421308Sache standard meaning for expanding a tilde fails. The function is called 10521308Sache with the text (sans tilde, as in "foo"), and returns a malloc()'ed string 10621308Sache which is the expansion, or a NULL pointer if there is no expansion. */ 10775406Sachetilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL; 10821308Sache 10921308Sache/* When non-null, this is a NULL terminated array of strings which 11021308Sache are duplicates for a tilde prefix. Bash uses this to expand 11121308Sache `=~' and `:~'. */ 11275406Sachechar **tilde_additional_prefixes = (char **)default_prefixes; 11321308Sache 11421308Sache/* When non-null, this is a NULL terminated array of strings which match 11521308Sache the end of a username, instead of just "/". Bash sets this to 11621308Sache `:' and `=~'. */ 11775406Sachechar **tilde_additional_suffixes = (char **)default_suffixes; 11821308Sache 119119610Sachestatic int tilde_find_prefix PARAMS((const char *, int *)); 120119610Sachestatic int tilde_find_suffix PARAMS((const char *)); 121119610Sachestatic char *isolate_tilde_prefix PARAMS((const char *, int *)); 122119610Sachestatic char *glue_prefix_and_suffix PARAMS((char *, const char *, int)); 123119610Sache 12421308Sache/* Find the start of a tilde expansion in STRING, and return the index of 12521308Sache the tilde which starts the expansion. Place the length of the text 12621308Sache which identified this tilde starter in LEN, excluding the tilde itself. */ 12721308Sachestatic int 12821308Sachetilde_find_prefix (string, len) 129119610Sache const char *string; 13021308Sache int *len; 13121308Sache{ 13221308Sache register int i, j, string_len; 13358310Sache register char **prefixes; 13421308Sache 13558310Sache prefixes = tilde_additional_prefixes; 13658310Sache 13721308Sache string_len = strlen (string); 13821308Sache *len = 0; 13921308Sache 14026497Sache if (*string == '\0' || *string == '~') 14121308Sache return (0); 14221308Sache 14321308Sache if (prefixes) 14421308Sache { 14521308Sache for (i = 0; i < string_len; i++) 14621308Sache { 14721308Sache for (j = 0; prefixes[j]; j++) 14821308Sache { 14921308Sache if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0) 15021308Sache { 15121308Sache *len = strlen (prefixes[j]) - 1; 15221308Sache return (i + *len); 15321308Sache } 15421308Sache } 15521308Sache } 15621308Sache } 15721308Sache return (string_len); 15821308Sache} 15921308Sache 16021308Sache/* Find the end of a tilde expansion in STRING, and return the index of 16121308Sache the character which ends the tilde definition. */ 16221308Sachestatic int 16321308Sachetilde_find_suffix (string) 164119610Sache const char *string; 16521308Sache{ 16621308Sache register int i, j, string_len; 16726497Sache register char **suffixes; 16821308Sache 16926497Sache suffixes = tilde_additional_suffixes; 17021308Sache string_len = strlen (string); 17121308Sache 17221308Sache for (i = 0; i < string_len; i++) 17321308Sache { 17458310Sache#if defined (__MSDOS__) 17558310Sache if (string[i] == '/' || string[i] == '\\' /* || !string[i] */) 17658310Sache#else 17726497Sache if (string[i] == '/' /* || !string[i] */) 17858310Sache#endif 17921308Sache break; 18021308Sache 18121308Sache for (j = 0; suffixes && suffixes[j]; j++) 18221308Sache { 18321308Sache if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0) 18421308Sache return (i); 18521308Sache } 18621308Sache } 18721308Sache return (i); 18821308Sache} 18921308Sache 19021308Sache/* Return a new string which is the result of tilde expanding STRING. */ 19121308Sachechar * 19221308Sachetilde_expand (string) 19375406Sache const char *string; 19421308Sache{ 19526497Sache char *result; 19621308Sache int result_size, result_index; 19721308Sache 19826497Sache result_index = result_size = 0; 19926497Sache if (result = strchr (string, '~')) 200119610Sache result = (char *)xmalloc (result_size = (strlen (string) + 16)); 20126497Sache else 202119610Sache result = (char *)xmalloc (result_size = (strlen (string) + 1)); 20321308Sache 20421308Sache /* Scan through STRING expanding tildes as we come to them. */ 20521308Sache while (1) 20621308Sache { 20721308Sache register int start, end; 20821308Sache char *tilde_word, *expansion; 20921308Sache int len; 21021308Sache 21121308Sache /* Make START point to the tilde which starts the expansion. */ 21221308Sache start = tilde_find_prefix (string, &len); 21321308Sache 21421308Sache /* Copy the skipped text into the result. */ 21521308Sache if ((result_index + start + 1) > result_size) 216119610Sache result = (char *)xrealloc (result, 1 + (result_size += (start + 20))); 21721308Sache 21821308Sache strncpy (result + result_index, string, start); 21921308Sache result_index += start; 22021308Sache 22121308Sache /* Advance STRING to the starting tilde. */ 22221308Sache string += start; 22321308Sache 22421308Sache /* Make END be the index of one after the last character of the 22521308Sache username. */ 22621308Sache end = tilde_find_suffix (string); 22721308Sache 22821308Sache /* If both START and END are zero, we are all done. */ 22921308Sache if (!start && !end) 23021308Sache break; 23121308Sache 23221308Sache /* Expand the entire tilde word, and copy it into RESULT. */ 233119610Sache tilde_word = (char *)xmalloc (1 + end); 23421308Sache strncpy (tilde_word, string, end); 23521308Sache tilde_word[end] = '\0'; 23621308Sache string += end; 23721308Sache 23821308Sache expansion = tilde_expand_word (tilde_word); 23921308Sache free (tilde_word); 24021308Sache 24121308Sache len = strlen (expansion); 24275406Sache#ifdef __CYGWIN__ 24358310Sache /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when 24475406Sache $HOME for `user' is /. On cygwin, // denotes a network drive. */ 24558310Sache if (len > 1 || *expansion != '/' || *string != '/') 24658310Sache#endif 24758310Sache { 24858310Sache if ((result_index + len + 1) > result_size) 249119610Sache result = (char *)xrealloc (result, 1 + (result_size += (len + 20))); 25021308Sache 25158310Sache strcpy (result + result_index, expansion); 25258310Sache result_index += len; 25358310Sache } 25421308Sache free (expansion); 25521308Sache } 25621308Sache 25721308Sache result[result_index] = '\0'; 25821308Sache 25921308Sache return (result); 26021308Sache} 26121308Sache 26226497Sache/* Take FNAME and return the tilde prefix we want expanded. If LENP is 26326497Sache non-null, the index of the end of the prefix into FNAME is returned in 26426497Sache the location it points to. */ 26526497Sachestatic char * 26626497Sacheisolate_tilde_prefix (fname, lenp) 267119610Sache const char *fname; 26826497Sache int *lenp; 26926497Sache{ 27026497Sache char *ret; 27126497Sache int i; 27226497Sache 273119610Sache ret = (char *)xmalloc (strlen (fname)); 27458310Sache#if defined (__MSDOS__) 27558310Sache for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++) 27658310Sache#else 27726497Sache for (i = 1; fname[i] && fname[i] != '/'; i++) 27858310Sache#endif 27926497Sache ret[i - 1] = fname[i]; 28026497Sache ret[i - 1] = '\0'; 28126497Sache if (lenp) 28226497Sache *lenp = i; 28326497Sache return ret; 28426497Sache} 28526497Sache 286157184Sache#if 0 287157184Sache/* Public function to scan a string (FNAME) beginning with a tilde and find 288157184Sache the portion of the string that should be passed to the tilde expansion 289157184Sache function. Right now, it just calls tilde_find_suffix and allocates new 290157184Sache memory, but it can be expanded to do different things later. */ 291157184Sachechar * 292157184Sachetilde_find_word (fname, flags, lenp) 293157184Sache const char *fname; 294157184Sache int flags, *lenp; 295157184Sache{ 296157184Sache int x; 297157184Sache char *r; 298157184Sache 299157184Sache x = tilde_find_suffix (fname); 300157184Sache if (x == 0) 301157184Sache { 302157184Sache r = savestring (fname); 303157184Sache if (lenp) 304157184Sache *lenp = 0; 305157184Sache } 306157184Sache else 307157184Sache { 308157184Sache r = (char *)xmalloc (1 + x); 309157184Sache strncpy (r, fname, x); 310157184Sache r[x] = '\0'; 311157184Sache if (lenp) 312157184Sache *lenp = x; 313157184Sache } 314157184Sache 315157184Sache return r; 316157184Sache} 317157184Sache#endif 318157184Sache 31926497Sache/* Return a string that is PREFIX concatenated with SUFFIX starting at 32026497Sache SUFFIND. */ 32126497Sachestatic char * 32226497Sacheglue_prefix_and_suffix (prefix, suffix, suffind) 323119610Sache char *prefix; 324119610Sache const char *suffix; 32526497Sache int suffind; 32626497Sache{ 32726497Sache char *ret; 32826497Sache int plen, slen; 32926497Sache 33026497Sache plen = (prefix && *prefix) ? strlen (prefix) : 0; 33126497Sache slen = strlen (suffix + suffind); 332119610Sache ret = (char *)xmalloc (plen + slen + 1); 33358310Sache if (plen) 33426497Sache strcpy (ret, prefix); 33526497Sache strcpy (ret + plen, suffix + suffind); 33626497Sache return ret; 33726497Sache} 33826497Sache 33921308Sache/* Do the work of tilde expansion on FILENAME. FILENAME starts with a 34026497Sache tilde. If there is no expansion, call tilde_expansion_failure_hook. 34126497Sache This always returns a newly-allocated string, never static storage. */ 34221308Sachechar * 34321308Sachetilde_expand_word (filename) 34475406Sache const char *filename; 34521308Sache{ 34626497Sache char *dirname, *expansion, *username; 34726497Sache int user_len; 34826497Sache struct passwd *user_entry; 34921308Sache 35026497Sache if (filename == 0) 35121308Sache return ((char *)NULL); 35221308Sache 35326497Sache if (*filename != '~') 35426497Sache return (savestring (filename)); 35521308Sache 35626497Sache /* A leading `~/' or a bare `~' is *always* translated to the value of 35726497Sache $HOME or the home directory of the current user, regardless of any 35826497Sache preexpansion hook. */ 35926497Sache if (filename[1] == '\0' || filename[1] == '/') 36021308Sache { 36126497Sache /* Prefix $HOME to the rest of the string. */ 36275406Sache expansion = sh_get_env_value ("HOME"); 36321308Sache 36421308Sache /* If there is no HOME variable, look up the directory in 36521308Sache the password database. */ 36626497Sache if (expansion == 0) 36775406Sache expansion = sh_get_home_dir (); 36821308Sache 36926497Sache return (glue_prefix_and_suffix (expansion, filename, 1)); 37021308Sache } 37121308Sache 37226497Sache username = isolate_tilde_prefix (filename, &user_len); 37321308Sache 37426497Sache if (tilde_expansion_preexpansion_hook) 37526497Sache { 37626497Sache expansion = (*tilde_expansion_preexpansion_hook) (username); 37726497Sache if (expansion) 37821308Sache { 37926497Sache dirname = glue_prefix_and_suffix (expansion, filename, user_len); 38026497Sache free (username); 38126497Sache free (expansion); 38226497Sache return (dirname); 38326497Sache } 38426497Sache } 38526497Sache 38626497Sache /* No preexpansion hook, or the preexpansion hook failed. Look in the 38726497Sache password database. */ 38826497Sache dirname = (char *)NULL; 389157184Sache#if defined (HAVE_GETPWNAM) 39026497Sache user_entry = getpwnam (username); 391157184Sache#else 392157184Sache user_entry = 0; 393157184Sache#endif 39426497Sache if (user_entry == 0) 39526497Sache { 39626497Sache /* If the calling program has a special syntax for expanding tildes, 39726497Sache and we couldn't find a standard expansion, then let them try. */ 39826497Sache if (tilde_expansion_failure_hook) 39926497Sache { 40026497Sache expansion = (*tilde_expansion_failure_hook) (username); 40126497Sache if (expansion) 40221308Sache { 40326497Sache dirname = glue_prefix_and_suffix (expansion, filename, user_len); 40426497Sache free (expansion); 40521308Sache } 40621308Sache } 40726497Sache /* If we don't have a failure hook, or if the failure hook did not 40826497Sache expand the tilde, return a copy of what we were passed. */ 40926497Sache if (dirname == 0) 41026497Sache dirname = savestring (filename); 41121308Sache } 412165670Sache#if defined (HAVE_GETPWENT) 41326497Sache else 414165670Sache dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len); 415165670Sache#endif 416165670Sache 417165670Sache free (username); 418157184Sache#if defined (HAVE_GETPWENT) 41926497Sache endpwent (); 420157184Sache#endif 42121308Sache return (dirname); 42221308Sache} 42321308Sache 42421308Sache 42521308Sache#if defined (TEST) 42621308Sache#undef NULL 42721308Sache#include <stdio.h> 42821308Sache 42921308Sachemain (argc, argv) 43021308Sache int argc; 43121308Sache char **argv; 43221308Sache{ 43321308Sache char *result, line[512]; 43421308Sache int done = 0; 43521308Sache 43621308Sache while (!done) 43721308Sache { 43821308Sache printf ("~expand: "); 43921308Sache fflush (stdout); 44021308Sache 44121308Sache if (!gets (line)) 44221308Sache strcpy (line, "done"); 44321308Sache 44421308Sache if ((strcmp (line, "done") == 0) || 44521308Sache (strcmp (line, "quit") == 0) || 44621308Sache (strcmp (line, "exit") == 0)) 44721308Sache { 44821308Sache done = 1; 44921308Sache break; 45021308Sache } 45121308Sache 45221308Sache result = tilde_expand (line); 45321308Sache printf (" --> %s\n", result); 45421308Sache free (result); 45521308Sache } 45621308Sache exit (0); 45721308Sache} 45821308Sache 45921308Sachestatic void memory_error_and_abort (); 46021308Sache 461119610Sachestatic void * 46221308Sachexmalloc (bytes) 463119610Sache size_t bytes; 46421308Sache{ 465119610Sache void *temp = (char *)malloc (bytes); 46621308Sache 46721308Sache if (!temp) 46821308Sache memory_error_and_abort (); 46921308Sache return (temp); 47021308Sache} 47121308Sache 472119610Sachestatic void * 47321308Sachexrealloc (pointer, bytes) 474119610Sache void *pointer; 47521308Sache int bytes; 47621308Sache{ 477119610Sache void *temp; 47821308Sache 47921308Sache if (!pointer) 480119610Sache temp = malloc (bytes); 48121308Sache else 482119610Sache temp = realloc (pointer, bytes); 48321308Sache 48421308Sache if (!temp) 48521308Sache memory_error_and_abort (); 48621308Sache 48721308Sache return (temp); 48821308Sache} 48921308Sache 49021308Sachestatic void 49121308Sachememory_error_and_abort () 49221308Sache{ 49321308Sache fprintf (stderr, "readline: out of virtual memory\n"); 49421308Sache abort (); 49521308Sache} 49621308Sache 49721308Sache/* 49821308Sache * Local variables: 49921308Sache * compile-command: "gcc -g -DTEST -o tilde tilde.c" 50021308Sache * end: 50121308Sache */ 50221308Sache#endif /* TEST */ 503