1672dff93Sespie /* tilde.c -- tilde expansion code (~/foo := $HOME/foo).
2*1076333cSespie $Id: tilde.c,v 1.4 2006/07/17 16:12:36 espie Exp $
3fbc94a17Sniklas
4*1076333cSespie Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1996, 1998, 1999,
5*1076333cSespie 2002, 2004 Free Software Foundation, Inc.
6fbc94a17Sniklas
7fbc94a17Sniklas This program is free software; you can redistribute it and/or modify
8fbc94a17Sniklas it under the terms of the GNU General Public License as published by
9fbc94a17Sniklas the Free Software Foundation; either version 2, or (at your option)
10fbc94a17Sniklas any later version.
11fbc94a17Sniklas
12fbc94a17Sniklas This program is distributed in the hope that it will be useful,
13fbc94a17Sniklas but WITHOUT ANY WARRANTY; without even the implied warranty of
14fbc94a17Sniklas MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15fbc94a17Sniklas GNU General Public License for more details.
16fbc94a17Sniklas
17fbc94a17Sniklas You should have received a copy of the GNU General Public License
18fbc94a17Sniklas along with this program; if not, write to the Free Software
19fbc94a17Sniklas Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20fbc94a17Sniklas
21fbc94a17Sniklas Written by Brian Fox (bfox@ai.mit.edu). */
22fbc94a17Sniklas
23840175f0Skstailey /* Include config.h before doing alloca. */
24840175f0Skstailey #include "info.h"
25*1076333cSespie #include "tilde.h"
26840175f0Skstailey
27fbc94a17Sniklas #if defined (TEST) || defined (STATIC_MALLOC)
28fbc94a17Sniklas static void *xmalloc (), *xrealloc ();
29fbc94a17Sniklas #endif /* TEST || STATIC_MALLOC */
30fbc94a17Sniklas
31fbc94a17Sniklas /* The default value of tilde_additional_prefixes. This is set to
32fbc94a17Sniklas whitespace preceding a tilde so that simple programs which do not
33fbc94a17Sniklas perform any word separation get desired behaviour. */
34fbc94a17Sniklas static char *default_prefixes[] =
35fbc94a17Sniklas { " ~", "\t~", (char *)NULL };
36fbc94a17Sniklas
37fbc94a17Sniklas /* The default value of tilde_additional_suffixes. This is set to
38fbc94a17Sniklas whitespace or newline so that simple programs which do not
39fbc94a17Sniklas perform any word separation get desired behaviour. */
40fbc94a17Sniklas static char *default_suffixes[] =
41fbc94a17Sniklas { " ", "\n", (char *)NULL };
42fbc94a17Sniklas
43fbc94a17Sniklas /* If non-null, this contains the address of a function to call if the
44fbc94a17Sniklas standard meaning for expanding a tilde fails. The function is called
45fbc94a17Sniklas with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
46fbc94a17Sniklas which is the expansion, or a NULL pointer if there is no expansion. */
47fbc94a17Sniklas CFunction *tilde_expansion_failure_hook = (CFunction *)NULL;
48fbc94a17Sniklas
49fbc94a17Sniklas /* When non-null, this is a NULL terminated array of strings which
50fbc94a17Sniklas are duplicates for a tilde prefix. Bash uses this to expand
51fbc94a17Sniklas `=~' and `:~'. */
52fbc94a17Sniklas char **tilde_additional_prefixes = default_prefixes;
53fbc94a17Sniklas
54fbc94a17Sniklas /* When non-null, this is a NULL terminated array of strings which match
55fbc94a17Sniklas the end of a username, instead of just "/". Bash sets this to
56fbc94a17Sniklas `:' and `=~'. */
57fbc94a17Sniklas char **tilde_additional_suffixes = default_suffixes;
58fbc94a17Sniklas
59fbc94a17Sniklas /* Find the start of a tilde expansion in STRING, and return the index of
60fbc94a17Sniklas the tilde which starts the expansion. Place the length of the text
61fbc94a17Sniklas which identified this tilde starter in LEN, excluding the tilde itself. */
62fbc94a17Sniklas static int
tilde_find_prefix(char * string,int * len)63*1076333cSespie tilde_find_prefix (char *string, int *len)
64fbc94a17Sniklas {
65fbc94a17Sniklas register int i, j, string_len;
66fbc94a17Sniklas register char **prefixes = tilde_additional_prefixes;
67fbc94a17Sniklas
68fbc94a17Sniklas string_len = strlen (string);
69fbc94a17Sniklas *len = 0;
70fbc94a17Sniklas
71fbc94a17Sniklas if (!*string || *string == '~')
72fbc94a17Sniklas return (0);
73fbc94a17Sniklas
74fbc94a17Sniklas if (prefixes)
75fbc94a17Sniklas {
76fbc94a17Sniklas for (i = 0; i < string_len; i++)
77fbc94a17Sniklas {
78fbc94a17Sniklas for (j = 0; prefixes[j]; j++)
79fbc94a17Sniklas {
80fbc94a17Sniklas if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
81fbc94a17Sniklas {
82fbc94a17Sniklas *len = strlen (prefixes[j]) - 1;
83fbc94a17Sniklas return (i + *len);
84fbc94a17Sniklas }
85fbc94a17Sniklas }
86fbc94a17Sniklas }
87fbc94a17Sniklas }
88fbc94a17Sniklas return (string_len);
89fbc94a17Sniklas }
90fbc94a17Sniklas
91fbc94a17Sniklas /* Find the end of a tilde expansion in STRING, and return the index of
92fbc94a17Sniklas the character which ends the tilde definition. */
93fbc94a17Sniklas static int
tilde_find_suffix(char * string)94*1076333cSespie tilde_find_suffix (char *string)
95fbc94a17Sniklas {
96fbc94a17Sniklas register int i, j, string_len;
97fbc94a17Sniklas register char **suffixes = tilde_additional_suffixes;
98fbc94a17Sniklas
99fbc94a17Sniklas string_len = strlen (string);
100fbc94a17Sniklas
101fbc94a17Sniklas for (i = 0; i < string_len; i++)
102fbc94a17Sniklas {
103672dff93Sespie if (IS_SLASH (string[i]) || !string[i])
104fbc94a17Sniklas break;
105fbc94a17Sniklas
106fbc94a17Sniklas for (j = 0; suffixes && suffixes[j]; j++)
107fbc94a17Sniklas {
108fbc94a17Sniklas if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
109fbc94a17Sniklas return (i);
110fbc94a17Sniklas }
111fbc94a17Sniklas }
112fbc94a17Sniklas return (i);
113fbc94a17Sniklas }
114fbc94a17Sniklas
115fbc94a17Sniklas /* Return a new string which is the result of tilde expanding STRING. */
116fbc94a17Sniklas char *
tilde_expand(char * string)117*1076333cSespie tilde_expand (char *string)
118fbc94a17Sniklas {
119*1076333cSespie char *result;
120fbc94a17Sniklas int result_size, result_index;
121fbc94a17Sniklas
122fbc94a17Sniklas result_size = result_index = 0;
123fbc94a17Sniklas result = (char *)NULL;
124fbc94a17Sniklas
125fbc94a17Sniklas /* Scan through STRING expanding tildes as we come to them. */
126fbc94a17Sniklas while (1)
127fbc94a17Sniklas {
128fbc94a17Sniklas register int start, end;
129fbc94a17Sniklas char *tilde_word, *expansion;
130fbc94a17Sniklas int len;
131fbc94a17Sniklas
132fbc94a17Sniklas /* Make START point to the tilde which starts the expansion. */
133fbc94a17Sniklas start = tilde_find_prefix (string, &len);
134fbc94a17Sniklas
135fbc94a17Sniklas /* Copy the skipped text into the result. */
136fbc94a17Sniklas if ((result_index + start + 1) > result_size)
137fbc94a17Sniklas result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
138fbc94a17Sniklas
139fbc94a17Sniklas strncpy (result + result_index, string, start);
140fbc94a17Sniklas result_index += start;
141fbc94a17Sniklas
142fbc94a17Sniklas /* Advance STRING to the starting tilde. */
143fbc94a17Sniklas string += start;
144fbc94a17Sniklas
145fbc94a17Sniklas /* Make END be the index of one after the last character of the
146fbc94a17Sniklas username. */
147fbc94a17Sniklas end = tilde_find_suffix (string);
148fbc94a17Sniklas
149fbc94a17Sniklas /* If both START and END are zero, we are all done. */
150fbc94a17Sniklas if (!start && !end)
151fbc94a17Sniklas break;
152fbc94a17Sniklas
153fbc94a17Sniklas /* Expand the entire tilde word, and copy it into RESULT. */
154fbc94a17Sniklas tilde_word = (char *)xmalloc (1 + end);
155fbc94a17Sniklas strncpy (tilde_word, string, end);
156fbc94a17Sniklas tilde_word[end] = '\0';
157fbc94a17Sniklas string += end;
158fbc94a17Sniklas
159fbc94a17Sniklas expansion = tilde_expand_word (tilde_word);
160fbc94a17Sniklas free (tilde_word);
161fbc94a17Sniklas
162fbc94a17Sniklas len = strlen (expansion);
163fbc94a17Sniklas if ((result_index + len + 1) > result_size)
164fbc94a17Sniklas result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
165fbc94a17Sniklas
166fbc94a17Sniklas strcpy (result + result_index, expansion);
167fbc94a17Sniklas result_index += len;
168fbc94a17Sniklas free (expansion);
169fbc94a17Sniklas }
170fbc94a17Sniklas
171fbc94a17Sniklas result[result_index] = '\0';
172fbc94a17Sniklas
173fbc94a17Sniklas return (result);
174fbc94a17Sniklas }
175fbc94a17Sniklas
176fbc94a17Sniklas /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
177fbc94a17Sniklas tilde. If there is no expansion, call tilde_expansion_failure_hook. */
178fbc94a17Sniklas char *
tilde_expand_word(char * filename)179*1076333cSespie tilde_expand_word (char *filename)
180fbc94a17Sniklas {
181672dff93Sespie char *dirname = filename ? xstrdup (filename) : NULL;
182fbc94a17Sniklas
183fbc94a17Sniklas if (dirname && *dirname == '~')
184fbc94a17Sniklas {
185fbc94a17Sniklas char *temp_name;
186672dff93Sespie if (!dirname[1] || IS_SLASH (dirname[1]))
187fbc94a17Sniklas {
188fbc94a17Sniklas /* Prepend $HOME to the rest of the string. */
18940248eceSdownsj char *temp_home = getenv ("HOME");
190fbc94a17Sniklas
191fbc94a17Sniklas /* If there is no HOME variable, look up the directory in
192fbc94a17Sniklas the password database. */
193fbc94a17Sniklas if (!temp_home)
194fbc94a17Sniklas {
195fbc94a17Sniklas struct passwd *entry;
196fbc94a17Sniklas
197fbc94a17Sniklas entry = (struct passwd *) getpwuid (getuid ());
198fbc94a17Sniklas if (entry)
199fbc94a17Sniklas temp_home = entry->pw_dir;
200fbc94a17Sniklas }
201fbc94a17Sniklas
202672dff93Sespie temp_name = xmalloc (1 + strlen (&dirname[1])
203fbc94a17Sniklas + (temp_home ? strlen (temp_home) : 0));
204fbc94a17Sniklas if (temp_home)
205fbc94a17Sniklas strcpy (temp_name, temp_home);
206672dff93Sespie else
207672dff93Sespie temp_name[0] = 0;
208fbc94a17Sniklas strcat (temp_name, &dirname[1]);
209fbc94a17Sniklas free (dirname);
210840175f0Skstailey dirname = xstrdup (temp_name);
211672dff93Sespie free (temp_name);
212fbc94a17Sniklas }
213fbc94a17Sniklas else
214fbc94a17Sniklas {
215fbc94a17Sniklas struct passwd *user_entry;
216672dff93Sespie char *username = xmalloc (257);
217fbc94a17Sniklas int i, c;
218fbc94a17Sniklas
219840175f0Skstailey for (i = 1; (c = dirname[i]); i++)
220fbc94a17Sniklas {
221672dff93Sespie if (IS_SLASH (c))
222fbc94a17Sniklas break;
223fbc94a17Sniklas else
224fbc94a17Sniklas username[i - 1] = c;
225fbc94a17Sniklas }
226672dff93Sespie username[i - 1] = 0;
227fbc94a17Sniklas
228fbc94a17Sniklas if (!(user_entry = (struct passwd *) getpwnam (username)))
229fbc94a17Sniklas {
230fbc94a17Sniklas /* If the calling program has a special syntax for
231fbc94a17Sniklas expanding tildes, and we couldn't find a standard
232fbc94a17Sniklas expansion, then let them try. */
233fbc94a17Sniklas if (tilde_expansion_failure_hook)
234fbc94a17Sniklas {
235*1076333cSespie char *expansion = (*tilde_expansion_failure_hook) (username);
236fbc94a17Sniklas
237fbc94a17Sniklas if (expansion)
238fbc94a17Sniklas {
239672dff93Sespie temp_name = xmalloc (1 + strlen (expansion)
240672dff93Sespie + strlen (&dirname[i]));
241fbc94a17Sniklas strcpy (temp_name, expansion);
242fbc94a17Sniklas strcat (temp_name, &dirname[i]);
243fbc94a17Sniklas free (expansion);
244fbc94a17Sniklas goto return_name;
245fbc94a17Sniklas }
246fbc94a17Sniklas }
247fbc94a17Sniklas /* We shouldn't report errors. */
248fbc94a17Sniklas }
249fbc94a17Sniklas else
250fbc94a17Sniklas {
251672dff93Sespie temp_name = xmalloc (1 + strlen (user_entry->pw_dir)
252672dff93Sespie + strlen (&dirname[i]));
253fbc94a17Sniklas strcpy (temp_name, user_entry->pw_dir);
254fbc94a17Sniklas strcat (temp_name, &dirname[i]);
255672dff93Sespie
256fbc94a17Sniklas return_name:
257fbc94a17Sniklas free (dirname);
258840175f0Skstailey dirname = xstrdup (temp_name);
259672dff93Sespie free (temp_name);
260fbc94a17Sniklas }
261672dff93Sespie
262fbc94a17Sniklas endpwent ();
263672dff93Sespie free (username);
264fbc94a17Sniklas }
265fbc94a17Sniklas }
266672dff93Sespie return dirname;
267fbc94a17Sniklas }
268fbc94a17Sniklas
269fbc94a17Sniklas
270fbc94a17Sniklas #if defined (TEST)
271fbc94a17Sniklas #undef NULL
272fbc94a17Sniklas #include <stdio.h>
273fbc94a17Sniklas
main(argc,argv)274fbc94a17Sniklas main (argc, argv)
275fbc94a17Sniklas int argc;
276fbc94a17Sniklas char **argv;
277fbc94a17Sniklas {
278fbc94a17Sniklas char *result, line[512];
279fbc94a17Sniklas int done = 0;
280fbc94a17Sniklas
281fbc94a17Sniklas while (!done)
282fbc94a17Sniklas {
283fbc94a17Sniklas printf ("~expand: ");
284fbc94a17Sniklas fflush (stdout);
285fbc94a17Sniklas
286fbc94a17Sniklas if (!gets (line))
287fbc94a17Sniklas strcpy (line, "done");
288fbc94a17Sniklas
289fbc94a17Sniklas if ((strcmp (line, "done") == 0) ||
290fbc94a17Sniklas (strcmp (line, "quit") == 0) ||
291fbc94a17Sniklas (strcmp (line, "exit") == 0))
292fbc94a17Sniklas {
293fbc94a17Sniklas done = 1;
294fbc94a17Sniklas break;
295fbc94a17Sniklas }
296fbc94a17Sniklas
297fbc94a17Sniklas result = tilde_expand (line);
298fbc94a17Sniklas printf (" --> %s\n", result);
299fbc94a17Sniklas free (result);
300fbc94a17Sniklas }
301672dff93Sespie xexit (0);
302fbc94a17Sniklas }
303fbc94a17Sniklas
304fbc94a17Sniklas static void memory_error_and_abort ();
305fbc94a17Sniklas
306fbc94a17Sniklas static void *
xmalloc(bytes)307fbc94a17Sniklas xmalloc (bytes)
308fbc94a17Sniklas int bytes;
309fbc94a17Sniklas {
310fbc94a17Sniklas void *temp = (void *)malloc (bytes);
311fbc94a17Sniklas
312fbc94a17Sniklas if (!temp)
313fbc94a17Sniklas memory_error_and_abort ();
314fbc94a17Sniklas return (temp);
315fbc94a17Sniklas }
316fbc94a17Sniklas
317fbc94a17Sniklas static void *
xrealloc(pointer,bytes)318fbc94a17Sniklas xrealloc (pointer, bytes)
319fbc94a17Sniklas void *pointer;
320fbc94a17Sniklas int bytes;
321fbc94a17Sniklas {
322fbc94a17Sniklas void *temp;
323fbc94a17Sniklas
324fbc94a17Sniklas if (!pointer)
325fbc94a17Sniklas temp = (char *)malloc (bytes);
326fbc94a17Sniklas else
327fbc94a17Sniklas temp = (char *)realloc (pointer, bytes);
328fbc94a17Sniklas
329fbc94a17Sniklas if (!temp)
330fbc94a17Sniklas memory_error_and_abort ();
331fbc94a17Sniklas
332fbc94a17Sniklas return (temp);
333fbc94a17Sniklas }
334fbc94a17Sniklas
335fbc94a17Sniklas static void
memory_error_and_abort()336fbc94a17Sniklas memory_error_and_abort ()
337fbc94a17Sniklas {
338840175f0Skstailey fprintf (stderr, _("readline: Out of virtual memory!\n"));
339fbc94a17Sniklas abort ();
340fbc94a17Sniklas }
341fbc94a17Sniklas #endif /* TEST */
342fbc94a17Sniklas
343