tilde.c revision 26497
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 1021308Sache Free Software Foundation; either version 1, 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 2021308Sache Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ 2121308Sache 2221308Sache#if defined (HAVE_CONFIG_H) 2321308Sache# include <config.h> 2421308Sache#endif 2521308Sache 2626497Sache#if defined (HAVE_UNISTD_H) 2726497Sache# include <unistd.h> 2826497Sache#endif 2926497Sache 3021308Sache#if defined (HAVE_STRING_H) 3121308Sache# include <string.h> 3221308Sache#else /* !HAVE_STRING_H */ 3321308Sache# include <strings.h> 3421308Sache#endif /* !HAVE_STRING_H */ 3521308Sache 3621308Sache#if defined (HAVE_STDLIB_H) 3721308Sache# include <stdlib.h> 3821308Sache#else 3921308Sache# include "ansi_stdlib.h" 4021308Sache#endif /* HAVE_STDLIB_H */ 4121308Sache 4221308Sache#include <sys/types.h> 4321308Sache#include <pwd.h> 4421308Sache 4521308Sache#include "tilde.h" 4621308Sache 4726497Sache#ifdef SHELL 4826497Sache#include "shell.h" 4926497Sache#endif 5026497Sache 5121308Sache#if !defined (HAVE_GETPW_DECLS) 5221308Sacheextern struct passwd *getpwuid (), *getpwnam (); 5321308Sache#endif /* !HAVE_GETPW_DECLS */ 5421308Sache 5521308Sache#if !defined (savestring) 5621308Sacheextern char *xmalloc (); 5721308Sache# ifndef strcpy 5821308Sacheextern char *strcpy (); 5921308Sache# endif 6021308Sache#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x)) 6121308Sache#endif /* !savestring */ 6221308Sache 6321308Sache#if !defined (NULL) 6421308Sache# if defined (__STDC__) 6521308Sache# define NULL ((void *) 0) 6621308Sache# else 6721308Sache# define NULL 0x0 6821308Sache# endif /* !__STDC__ */ 6921308Sache#endif /* !NULL */ 7021308Sache 7121308Sache#if defined (TEST) || defined (STATIC_MALLOC) 7221308Sachestatic char *xmalloc (), *xrealloc (); 7321308Sache#else 7421308Sacheextern char *xmalloc (), *xrealloc (); 7521308Sache#endif /* TEST || STATIC_MALLOC */ 7621308Sache 7721308Sache/* The default value of tilde_additional_prefixes. This is set to 7821308Sache whitespace preceding a tilde so that simple programs which do not 7921308Sache perform any word separation get desired behaviour. */ 8021308Sachestatic char *default_prefixes[] = 8121308Sache { " ~", "\t~", (char *)NULL }; 8221308Sache 8321308Sache/* The default value of tilde_additional_suffixes. This is set to 8421308Sache whitespace or newline so that simple programs which do not 8521308Sache perform any word separation get desired behaviour. */ 8621308Sachestatic char *default_suffixes[] = 8721308Sache { " ", "\n", (char *)NULL }; 8821308Sache 8926497Sache/* If non-null, this contains the address of a function that the application 9026497Sache wants called before trying the standard tilde expansions. The function 9126497Sache is called with the text sans tilde, and returns a malloc()'ed string 9226497Sache which is the expansion, or a NULL pointer if the expansion fails. */ 9326497SacheCPFunction *tilde_expansion_preexpansion_hook = (CPFunction *)NULL; 9426497Sache 9521308Sache/* If non-null, this contains the address of a function to call if the 9621308Sache standard meaning for expanding a tilde fails. The function is called 9721308Sache with the text (sans tilde, as in "foo"), and returns a malloc()'ed string 9821308Sache which is the expansion, or a NULL pointer if there is no expansion. */ 9921308SacheCPFunction *tilde_expansion_failure_hook = (CPFunction *)NULL; 10021308Sache 10121308Sache/* When non-null, this is a NULL terminated array of strings which 10221308Sache are duplicates for a tilde prefix. Bash uses this to expand 10321308Sache `=~' and `:~'. */ 10421308Sachechar **tilde_additional_prefixes = default_prefixes; 10521308Sache 10621308Sache/* When non-null, this is a NULL terminated array of strings which match 10721308Sache the end of a username, instead of just "/". Bash sets this to 10821308Sache `:' and `=~'. */ 10921308Sachechar **tilde_additional_suffixes = default_suffixes; 11021308Sache 11121308Sache/* Find the start of a tilde expansion in STRING, and return the index of 11221308Sache the tilde which starts the expansion. Place the length of the text 11321308Sache which identified this tilde starter in LEN, excluding the tilde itself. */ 11421308Sachestatic int 11521308Sachetilde_find_prefix (string, len) 11621308Sache char *string; 11721308Sache int *len; 11821308Sache{ 11921308Sache register int i, j, string_len; 12021308Sache register char **prefixes = tilde_additional_prefixes; 12121308Sache 12221308Sache string_len = strlen (string); 12321308Sache *len = 0; 12421308Sache 12526497Sache if (*string == '\0' || *string == '~') 12621308Sache return (0); 12721308Sache 12821308Sache if (prefixes) 12921308Sache { 13021308Sache for (i = 0; i < string_len; i++) 13121308Sache { 13221308Sache for (j = 0; prefixes[j]; j++) 13321308Sache { 13421308Sache if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0) 13521308Sache { 13621308Sache *len = strlen (prefixes[j]) - 1; 13721308Sache return (i + *len); 13821308Sache } 13921308Sache } 14021308Sache } 14121308Sache } 14221308Sache return (string_len); 14321308Sache} 14421308Sache 14521308Sache/* Find the end of a tilde expansion in STRING, and return the index of 14621308Sache the character which ends the tilde definition. */ 14721308Sachestatic int 14821308Sachetilde_find_suffix (string) 14921308Sache char *string; 15021308Sache{ 15121308Sache register int i, j, string_len; 15226497Sache register char **suffixes; 15321308Sache 15426497Sache suffixes = tilde_additional_suffixes; 15521308Sache string_len = strlen (string); 15621308Sache 15721308Sache for (i = 0; i < string_len; i++) 15821308Sache { 15926497Sache if (string[i] == '/' /* || !string[i] */) 16021308Sache break; 16121308Sache 16221308Sache for (j = 0; suffixes && suffixes[j]; j++) 16321308Sache { 16421308Sache if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0) 16521308Sache return (i); 16621308Sache } 16721308Sache } 16821308Sache return (i); 16921308Sache} 17021308Sache 17126497Sache#if !defined (SHELL) 17226497Sachestatic char * 17326497Sacheget_string_value (varname) 17426497Sache char *varname; 17526497Sache{ 17626497Sache return ((char *)getenv (varname)); 17726497Sache} 17826497Sache#endif 17926497Sache 18021308Sache/* Return a new string which is the result of tilde expanding STRING. */ 18121308Sachechar * 18221308Sachetilde_expand (string) 18321308Sache char *string; 18421308Sache{ 18526497Sache char *result; 18621308Sache int result_size, result_index; 18721308Sache 18826497Sache result_index = result_size = 0; 18926497Sache if (result = strchr (string, '~')) 19026497Sache result = xmalloc (result_size = (strlen (string) + 16)); 19126497Sache else 19226497Sache result = xmalloc (result_size = strlen (string)); 19321308Sache 19421308Sache /* Scan through STRING expanding tildes as we come to them. */ 19521308Sache while (1) 19621308Sache { 19721308Sache register int start, end; 19821308Sache char *tilde_word, *expansion; 19921308Sache int len; 20021308Sache 20121308Sache /* Make START point to the tilde which starts the expansion. */ 20221308Sache start = tilde_find_prefix (string, &len); 20321308Sache 20421308Sache /* Copy the skipped text into the result. */ 20521308Sache if ((result_index + start + 1) > result_size) 20621308Sache result = xrealloc (result, 1 + (result_size += (start + 20))); 20721308Sache 20821308Sache strncpy (result + result_index, string, start); 20921308Sache result_index += start; 21021308Sache 21121308Sache /* Advance STRING to the starting tilde. */ 21221308Sache string += start; 21321308Sache 21421308Sache /* Make END be the index of one after the last character of the 21521308Sache username. */ 21621308Sache end = tilde_find_suffix (string); 21721308Sache 21821308Sache /* If both START and END are zero, we are all done. */ 21921308Sache if (!start && !end) 22021308Sache break; 22121308Sache 22221308Sache /* Expand the entire tilde word, and copy it into RESULT. */ 22321308Sache tilde_word = xmalloc (1 + end); 22421308Sache strncpy (tilde_word, string, end); 22521308Sache tilde_word[end] = '\0'; 22621308Sache string += end; 22721308Sache 22821308Sache expansion = tilde_expand_word (tilde_word); 22921308Sache free (tilde_word); 23021308Sache 23121308Sache len = strlen (expansion); 23221308Sache if ((result_index + len + 1) > result_size) 23321308Sache result = xrealloc (result, 1 + (result_size += (len + 20))); 23421308Sache 23521308Sache strcpy (result + result_index, expansion); 23621308Sache result_index += len; 23721308Sache free (expansion); 23821308Sache } 23921308Sache 24021308Sache result[result_index] = '\0'; 24121308Sache 24221308Sache return (result); 24321308Sache} 24421308Sache 24526497Sache/* Take FNAME and return the tilde prefix we want expanded. If LENP is 24626497Sache non-null, the index of the end of the prefix into FNAME is returned in 24726497Sache the location it points to. */ 24826497Sachestatic char * 24926497Sacheisolate_tilde_prefix (fname, lenp) 25026497Sache char *fname; 25126497Sache int *lenp; 25226497Sache{ 25326497Sache char *ret; 25426497Sache int i; 25526497Sache 25626497Sache ret = xmalloc (strlen (fname)); 25726497Sache for (i = 1; fname[i] && fname[i] != '/'; i++) 25826497Sache ret[i - 1] = fname[i]; 25926497Sache ret[i - 1] = '\0'; 26026497Sache if (lenp) 26126497Sache *lenp = i; 26226497Sache return ret; 26326497Sache} 26426497Sache 26526497Sache/* Return a string that is PREFIX concatenated with SUFFIX starting at 26626497Sache SUFFIND. */ 26726497Sachestatic char * 26826497Sacheglue_prefix_and_suffix (prefix, suffix, suffind) 26926497Sache char *prefix, *suffix; 27026497Sache int suffind; 27126497Sache{ 27226497Sache char *ret; 27326497Sache int plen, slen; 27426497Sache 27526497Sache plen = (prefix && *prefix) ? strlen (prefix) : 0; 27626497Sache slen = strlen (suffix + suffind); 27726497Sache ret = xmalloc (plen + slen + 1); 27826497Sache if (prefix && *prefix) 27926497Sache strcpy (ret, prefix); 28026497Sache strcpy (ret + plen, suffix + suffind); 28126497Sache return ret; 28226497Sache} 28326497Sache 28426497Sachestatic char * 28526497Sacheget_home_dir () 28626497Sache{ 28726497Sache char *home_dir; 28826497Sache 28926497Sache#ifdef SHELL 29026497Sache home_dir = (char *)NULL; 29126497Sache if (current_user.home_dir == 0) 29226497Sache get_current_user_info (); 29326497Sache home_dir = current_user.home_dir; 29426497Sache#else 29526497Sache struct passwd *entry; 29626497Sache 29726497Sache home_dir = (char *)NULL; 29826497Sache entry = getpwuid (getuid ()); 29926497Sache if (entry) 30026497Sache home_dir = entry->pw_dir; 30126497Sache#endif 30226497Sache return (home_dir); 30326497Sache} 30426497Sache 30521308Sache/* Do the work of tilde expansion on FILENAME. FILENAME starts with a 30626497Sache tilde. If there is no expansion, call tilde_expansion_failure_hook. 30726497Sache This always returns a newly-allocated string, never static storage. */ 30821308Sachechar * 30921308Sachetilde_expand_word (filename) 31021308Sache char *filename; 31121308Sache{ 31226497Sache char *dirname, *expansion, *username; 31326497Sache int user_len; 31426497Sache struct passwd *user_entry; 31521308Sache 31626497Sache if (filename == 0) 31721308Sache return ((char *)NULL); 31821308Sache 31926497Sache if (*filename != '~') 32026497Sache return (savestring (filename)); 32121308Sache 32226497Sache /* A leading `~/' or a bare `~' is *always* translated to the value of 32326497Sache $HOME or the home directory of the current user, regardless of any 32426497Sache preexpansion hook. */ 32526497Sache if (filename[1] == '\0' || filename[1] == '/') 32621308Sache { 32726497Sache /* Prefix $HOME to the rest of the string. */ 32826497Sache expansion = get_string_value ("HOME"); 32921308Sache 33021308Sache /* If there is no HOME variable, look up the directory in 33121308Sache the password database. */ 33226497Sache if (expansion == 0) 33326497Sache expansion = get_home_dir (); 33421308Sache 33526497Sache return (glue_prefix_and_suffix (expansion, filename, 1)); 33621308Sache } 33721308Sache 33826497Sache username = isolate_tilde_prefix (filename, &user_len); 33921308Sache 34026497Sache if (tilde_expansion_preexpansion_hook) 34126497Sache { 34226497Sache expansion = (*tilde_expansion_preexpansion_hook) (username); 34326497Sache if (expansion) 34421308Sache { 34526497Sache dirname = glue_prefix_and_suffix (expansion, filename, user_len); 34626497Sache free (username); 34726497Sache free (expansion); 34826497Sache return (dirname); 34926497Sache } 35026497Sache } 35126497Sache 35226497Sache /* No preexpansion hook, or the preexpansion hook failed. Look in the 35326497Sache password database. */ 35426497Sache dirname = (char *)NULL; 35526497Sache user_entry = getpwnam (username); 35626497Sache if (user_entry == 0) 35726497Sache { 35826497Sache /* If the calling program has a special syntax for expanding tildes, 35926497Sache and we couldn't find a standard expansion, then let them try. */ 36026497Sache if (tilde_expansion_failure_hook) 36126497Sache { 36226497Sache expansion = (*tilde_expansion_failure_hook) (username); 36326497Sache if (expansion) 36421308Sache { 36526497Sache dirname = glue_prefix_and_suffix (expansion, filename, user_len); 36626497Sache free (expansion); 36721308Sache } 36821308Sache } 36921308Sache free (username); 37026497Sache /* If we don't have a failure hook, or if the failure hook did not 37126497Sache expand the tilde, return a copy of what we were passed. */ 37226497Sache if (dirname == 0) 37326497Sache dirname = savestring (filename); 37421308Sache } 37526497Sache else 37626497Sache { 37726497Sache free (username); 37826497Sache dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len); 37926497Sache } 38021308Sache 38126497Sache endpwent (); 38221308Sache return (dirname); 38321308Sache} 38421308Sache 38521308Sache 38621308Sache#if defined (TEST) 38721308Sache#undef NULL 38821308Sache#include <stdio.h> 38921308Sache 39021308Sachemain (argc, argv) 39121308Sache int argc; 39221308Sache char **argv; 39321308Sache{ 39421308Sache char *result, line[512]; 39521308Sache int done = 0; 39621308Sache 39721308Sache while (!done) 39821308Sache { 39921308Sache printf ("~expand: "); 40021308Sache fflush (stdout); 40121308Sache 40221308Sache if (!gets (line)) 40321308Sache strcpy (line, "done"); 40421308Sache 40521308Sache if ((strcmp (line, "done") == 0) || 40621308Sache (strcmp (line, "quit") == 0) || 40721308Sache (strcmp (line, "exit") == 0)) 40821308Sache { 40921308Sache done = 1; 41021308Sache break; 41121308Sache } 41221308Sache 41321308Sache result = tilde_expand (line); 41421308Sache printf (" --> %s\n", result); 41521308Sache free (result); 41621308Sache } 41721308Sache exit (0); 41821308Sache} 41921308Sache 42021308Sachestatic void memory_error_and_abort (); 42121308Sache 42221308Sachestatic char * 42321308Sachexmalloc (bytes) 42421308Sache int bytes; 42521308Sache{ 42621308Sache char *temp = (char *)malloc (bytes); 42721308Sache 42821308Sache if (!temp) 42921308Sache memory_error_and_abort (); 43021308Sache return (temp); 43121308Sache} 43221308Sache 43321308Sachestatic char * 43421308Sachexrealloc (pointer, bytes) 43521308Sache char *pointer; 43621308Sache int bytes; 43721308Sache{ 43821308Sache char *temp; 43921308Sache 44021308Sache if (!pointer) 44121308Sache temp = (char *)malloc (bytes); 44221308Sache else 44321308Sache temp = (char *)realloc (pointer, bytes); 44421308Sache 44521308Sache if (!temp) 44621308Sache memory_error_and_abort (); 44721308Sache 44821308Sache return (temp); 44921308Sache} 45021308Sache 45121308Sachestatic void 45221308Sachememory_error_and_abort () 45321308Sache{ 45421308Sache fprintf (stderr, "readline: out of virtual memory\n"); 45521308Sache abort (); 45621308Sache} 45721308Sache 45821308Sache/* 45921308Sache * Local variables: 46021308Sache * compile-command: "gcc -g -DTEST -o tilde tilde.c" 46121308Sache * end: 46221308Sache */ 46321308Sache#endif /* TEST */ 464