1 /* tilde.c -- tilde expansion code (~/foo := $HOME/foo).
2 $Id: tilde.c,v 1.8 2008/06/11 09:55:43 gray Exp $
4 Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1996, 1998, 1999,
5 2002, 2004, 2006, 2007, 2008 Free Software Foundation, Inc.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Written by Brian Fox (bfox@ai.mit.edu). */
25 #if defined (TEST) || defined (STATIC_MALLOC)
26 static void *xmalloc (), *xrealloc ();
27 #endif /* TEST || STATIC_MALLOC */
29 /* The default value of tilde_additional_prefixes. This is set to
30 whitespace preceding a tilde so that simple programs which do not
31 perform any word separation get desired behaviour. */
32 static char *default_prefixes[] =
33 { " ~", "\t~", NULL };
35 /* The default value of tilde_additional_suffixes. This is set to
36 whitespace or newline so that simple programs which do not
37 perform any word separation get desired behaviour. */
38 static char *default_suffixes[] =
41 /* If non-null, this contains the address of a function to call if the
42 standard meaning for expanding a tilde fails. The function is called
43 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
44 which is the expansion, or a NULL pointer if there is no expansion. */
45 CFunction *tilde_expansion_failure_hook = NULL;
47 /* When non-null, this is a NULL terminated array of strings which
48 are duplicates for a tilde prefix. Bash uses this to expand
50 char **tilde_additional_prefixes = default_prefixes;
52 /* When non-null, this is a NULL terminated array of strings which match
53 the end of a username, instead of just "/". Bash sets this to
55 char **tilde_additional_suffixes = default_suffixes;
57 /* Find the start of a tilde expansion in STRING, and return the index of
58 the tilde which starts the expansion. Place the length of the text
59 which identified this tilde starter in LEN, excluding the tilde itself. */
61 tilde_find_prefix (char *string, int *len)
63 register int i, j, string_len;
64 register char **prefixes = tilde_additional_prefixes;
66 string_len = strlen (string);
69 if (!*string || *string == '~')
74 for (i = 0; i < string_len; i++)
76 for (j = 0; prefixes[j]; j++)
78 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
80 *len = strlen (prefixes[j]) - 1;
89 /* Find the end of a tilde expansion in STRING, and return the index of
90 the character which ends the tilde definition. */
92 tilde_find_suffix (char *string)
94 register int i, j, string_len;
95 register char **suffixes = tilde_additional_suffixes;
97 string_len = strlen (string);
99 for (i = 0; i < string_len; i++)
101 if (IS_SLASH (string[i]) || !string[i])
104 for (j = 0; suffixes && suffixes[j]; j++)
106 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
113 /* Return a new string which is the result of tilde expanding STRING. */
115 tilde_expand (char *string)
118 int result_size, result_index;
120 result_size = result_index = 0;
123 /* Scan through STRING expanding tildes as we come to them. */
126 register int start, end;
127 char *tilde_word, *expansion;
130 /* Make START point to the tilde which starts the expansion. */
131 start = tilde_find_prefix (string, &len);
133 /* Copy the skipped text into the result. */
134 if ((result_index + start + 1) > result_size)
135 result = xrealloc (result, 1 + (result_size += (start + 20)));
137 strncpy (result + result_index, string, start);
138 result_index += start;
140 /* Advance STRING to the starting tilde. */
143 /* Make END be the index of one after the last character of the
145 end = tilde_find_suffix (string);
147 /* If both START and END are zero, we are all done. */
151 /* Expand the entire tilde word, and copy it into RESULT. */
152 tilde_word = xmalloc (1 + end);
153 strncpy (tilde_word, string, end);
154 tilde_word[end] = '\0';
157 expansion = tilde_expand_word (tilde_word);
160 len = strlen (expansion);
161 if ((result_index + len + 1) > result_size)
162 result = xrealloc (result, 1 + (result_size += (len + 20)));
164 strcpy (result + result_index, expansion);
169 result[result_index] = '\0';
174 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
175 tilde. If there is no expansion, call tilde_expansion_failure_hook. */
177 tilde_expand_word (char *filename)
179 char *dirname = filename ? xstrdup (filename) : NULL;
181 if (dirname && *dirname == '~')
184 if (!dirname[1] || IS_SLASH (dirname[1]))
186 /* Prepend $HOME to the rest of the string. */
187 char *temp_home = getenv ("HOME");
189 /* If there is no HOME variable, look up the directory in
190 the password database. */
193 struct passwd *entry;
195 entry = (struct passwd *) getpwuid (getuid ());
197 temp_home = entry->pw_dir;
200 temp_name = xmalloc (1 + strlen (&dirname[1])
201 + (temp_home ? strlen (temp_home) : 0));
203 strcpy (temp_name, temp_home);
206 strcat (temp_name, &dirname[1]);
208 dirname = xstrdup (temp_name);
213 struct passwd *user_entry;
214 char *username = xmalloc (257);
217 for (i = 1; (c = dirname[i]); i++)
226 if (!(user_entry = (struct passwd *) getpwnam (username)))
228 /* If the calling program has a special syntax for
229 expanding tildes, and we couldn't find a standard
230 expansion, then let them try. */
231 if (tilde_expansion_failure_hook)
233 char *expansion = (*tilde_expansion_failure_hook) (username);
237 temp_name = xmalloc (1 + strlen (expansion)
238 + strlen (&dirname[i]));
239 strcpy (temp_name, expansion);
240 strcat (temp_name, &dirname[i]);
245 /* We shouldn't report errors. */
249 temp_name = xmalloc (1 + strlen (user_entry->pw_dir)
250 + strlen (&dirname[i]));
251 strcpy (temp_name, user_entry->pw_dir);
252 strcat (temp_name, &dirname[i]);
256 dirname = xstrdup (temp_name);
276 char *result, line[512];
281 printf ("~expand: ");
285 strcpy (line, "done");
287 if ((strcmp (line, "done") == 0) ||
288 (strcmp (line, "quit") == 0) ||
289 (strcmp (line, "exit") == 0))
295 result = tilde_expand (line);
296 printf (" --> %s\n", result);
302 static void memory_error_and_abort ();
308 void *temp = (void *)malloc (bytes);
311 memory_error_and_abort ();
316 xrealloc (pointer, bytes)
323 temp = (char *)malloc (bytes);
325 temp = (char *)realloc (pointer, bytes);
328 memory_error_and_abort ();
334 memory_error_and_abort ()
336 fprintf (stderr, _("readline: Out of virtual memory!\n"));