1 /* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
3 /* Copyright (C) 1988,1989 Free Software Foundation, Inc.
5 This file is part of GNU Readline, a library for reading lines
6 of text with interactive input and history editing.
8 Readline is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any
13 Readline is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Readline; see the file COPYING. If not, write to the Free
20 Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22 #if defined (HAVE_CONFIG_H)
26 #if defined (HAVE_UNISTD_H)
28 # include <sys/types.h>
33 #if defined (HAVE_STRING_H)
35 #else /* !HAVE_STRING_H */
37 #endif /* !HAVE_STRING_H */
39 #if defined (HAVE_STDLIB_H)
42 # include "ansi_stdlib.h"
43 #endif /* HAVE_STDLIB_H */
45 #include <sys/types.h>
50 #if defined (TEST) || defined (STATIC_MALLOC)
51 static void *xmalloc (), *xrealloc ();
54 #endif /* TEST || STATIC_MALLOC */
56 #if !defined (HAVE_GETPW_DECLS)
57 extern struct passwd *getpwuid PARAMS((uid_t));
58 extern struct passwd *getpwnam PARAMS((const char *));
59 #endif /* !HAVE_GETPW_DECLS */
61 #if !defined (savestring)
63 extern char *strcpy ();
65 #define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
66 #endif /* !savestring */
69 # if defined (__STDC__)
70 # define NULL ((void *) 0)
73 # endif /* !__STDC__ */
76 /* If being compiled as part of bash, these will be satisfied from
77 variables.o. If being compiled as part of readline, they will
78 be satisfied from shell.o. */
79 extern char *sh_get_home_dir PARAMS((void));
80 extern char *sh_get_env_value PARAMS((const char *));
82 /* The default value of tilde_additional_prefixes. This is set to
83 whitespace preceding a tilde so that simple programs which do not
84 perform any word separation get desired behaviour. */
85 static const char *default_prefixes[] =
86 { " ~", "\t~", (const char *)NULL };
88 /* The default value of tilde_additional_suffixes. This is set to
89 whitespace or newline so that simple programs which do not
90 perform any word separation get desired behaviour. */
91 static const char *default_suffixes[] =
92 { " ", "\n", (const char *)NULL };
94 /* If non-null, this contains the address of a function that the application
95 wants called before trying the standard tilde expansions. The function
96 is called with the text sans tilde, and returns a malloc()'ed string
97 which is the expansion, or a NULL pointer if the expansion fails. */
98 tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
100 /* If non-null, this contains the address of a function to call if the
101 standard meaning for expanding a tilde fails. The function is called
102 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
103 which is the expansion, or a NULL pointer if there is no expansion. */
104 tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
106 /* When non-null, this is a NULL terminated array of strings which
107 are duplicates for a tilde prefix. Bash uses this to expand
109 char **tilde_additional_prefixes = (char **)default_prefixes;
111 /* When non-null, this is a NULL terminated array of strings which match
112 the end of a username, instead of just "/". Bash sets this to
114 char **tilde_additional_suffixes = (char **)default_suffixes;
116 static int tilde_find_prefix PARAMS((const char *, int *));
117 static int tilde_find_suffix PARAMS((const char *));
118 static char *isolate_tilde_prefix PARAMS((const char *, int *));
119 static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
121 /* Find the start of a tilde expansion in STRING, and return the index of
122 the tilde which starts the expansion. Place the length of the text
123 which identified this tilde starter in LEN, excluding the tilde itself. */
125 tilde_find_prefix (string, len)
129 register int i, j, string_len;
130 register char **prefixes;
132 prefixes = tilde_additional_prefixes;
134 string_len = strlen (string);
137 if (*string == '\0' || *string == '~')
142 for (i = 0; i < string_len; i++)
144 for (j = 0; prefixes[j]; j++)
146 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
148 *len = strlen (prefixes[j]) - 1;
157 /* Find the end of a tilde expansion in STRING, and return the index of
158 the character which ends the tilde definition. */
160 tilde_find_suffix (string)
163 register int i, j, string_len;
164 register char **suffixes;
166 suffixes = tilde_additional_suffixes;
167 string_len = strlen (string);
169 for (i = 0; i < string_len; i++)
171 #if defined (__MSDOS__)
172 if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
174 if (string[i] == '/' /* || !string[i] */)
178 for (j = 0; suffixes && suffixes[j]; j++)
180 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
187 /* Return a new string which is the result of tilde expanding STRING. */
189 tilde_expand (string)
193 int result_size, result_index;
195 result_index = result_size = 0;
196 if (result = strchr (string, '~'))
197 result = (char *)xmalloc (result_size = (strlen (string) + 16));
199 result = (char *)xmalloc (result_size = (strlen (string) + 1));
201 /* Scan through STRING expanding tildes as we come to them. */
204 register int start, end;
205 char *tilde_word, *expansion;
208 /* Make START point to the tilde which starts the expansion. */
209 start = tilde_find_prefix (string, &len);
211 /* Copy the skipped text into the result. */
212 if ((result_index + start + 1) > result_size)
213 result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
215 strncpy (result + result_index, string, start);
216 result_index += start;
218 /* Advance STRING to the starting tilde. */
221 /* Make END be the index of one after the last character of the
223 end = tilde_find_suffix (string);
225 /* If both START and END are zero, we are all done. */
229 /* Expand the entire tilde word, and copy it into RESULT. */
230 tilde_word = (char *)xmalloc (1 + end);
231 strncpy (tilde_word, string, end);
232 tilde_word[end] = '\0';
235 expansion = tilde_expand_word (tilde_word);
238 len = strlen (expansion);
240 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
241 $HOME for `user' is /. On cygwin, // denotes a network drive. */
242 if (len > 1 || *expansion != '/' || *string != '/')
245 if ((result_index + len + 1) > result_size)
246 result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
248 strcpy (result + result_index, expansion);
254 result[result_index] = '\0';
259 /* Take FNAME and return the tilde prefix we want expanded. If LENP is
260 non-null, the index of the end of the prefix into FNAME is returned in
261 the location it points to. */
263 isolate_tilde_prefix (fname, lenp)
270 ret = (char *)xmalloc (strlen (fname));
271 #if defined (__MSDOS__)
272 for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
274 for (i = 1; fname[i] && fname[i] != '/'; i++)
276 ret[i - 1] = fname[i];
283 /* Return a string that is PREFIX concatenated with SUFFIX starting at
286 glue_prefix_and_suffix (prefix, suffix, suffind)
294 plen = (prefix && *prefix) ? strlen (prefix) : 0;
295 slen = strlen (suffix + suffind);
296 ret = (char *)xmalloc (plen + slen + 1);
298 strcpy (ret, prefix);
299 strcpy (ret + plen, suffix + suffind);
303 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
304 tilde. If there is no expansion, call tilde_expansion_failure_hook.
305 This always returns a newly-allocated string, never static storage. */
307 tilde_expand_word (filename)
308 const char *filename;
310 char *dirname, *expansion, *username;
312 struct passwd *user_entry;
315 return ((char *)NULL);
317 if (*filename != '~')
318 return (savestring (filename));
320 /* A leading `~/' or a bare `~' is *always* translated to the value of
321 $HOME or the home directory of the current user, regardless of any
322 preexpansion hook. */
323 if (filename[1] == '\0' || filename[1] == '/')
325 /* Prefix $HOME to the rest of the string. */
326 expansion = sh_get_env_value ("HOME");
328 /* If there is no HOME variable, look up the directory in
329 the password database. */
331 expansion = sh_get_home_dir ();
333 return (glue_prefix_and_suffix (expansion, filename, 1));
336 username = isolate_tilde_prefix (filename, &user_len);
338 if (tilde_expansion_preexpansion_hook)
340 expansion = (*tilde_expansion_preexpansion_hook) (username);
343 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
350 /* No preexpansion hook, or the preexpansion hook failed. Look in the
351 password database. */
352 dirname = (char *)NULL;
353 user_entry = getpwnam (username);
356 /* If the calling program has a special syntax for expanding tildes,
357 and we couldn't find a standard expansion, then let them try. */
358 if (tilde_expansion_failure_hook)
360 expansion = (*tilde_expansion_failure_hook) (username);
363 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
368 /* If we don't have a failure hook, or if the failure hook did not
369 expand the tilde, return a copy of what we were passed. */
371 dirname = savestring (filename);
376 dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
392 char *result, line[512];
397 printf ("~expand: ");
401 strcpy (line, "done");
403 if ((strcmp (line, "done") == 0) ||
404 (strcmp (line, "quit") == 0) ||
405 (strcmp (line, "exit") == 0))
411 result = tilde_expand (line);
412 printf (" --> %s\n", result);
418 static void memory_error_and_abort ();
424 void *temp = (char *)malloc (bytes);
427 memory_error_and_abort ();
432 xrealloc (pointer, bytes)
439 temp = malloc (bytes);
441 temp = realloc (pointer, bytes);
444 memory_error_and_abort ();
450 memory_error_and_abort ()
452 fprintf (stderr, "readline: out of virtual memory\n");
458 * compile-command: "gcc -g -DTEST -o tilde tilde.c"