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>
48 #endif /* HAVE_PWD_H */
52 #if defined (TEST) || defined (STATIC_MALLOC)
53 static void *xmalloc (), *xrealloc ();
56 #endif /* TEST || STATIC_MALLOC */
58 #if defined (HAVE_GETPWNAM) && !defined (HAVE_GETPW_DECLS)
59 extern struct passwd
*getpwuid
PARAMS((uid_t
));
60 extern struct passwd
*getpwnam
PARAMS((const char *));
61 #endif /* defined (HAVE_GETPWNAM) && !HAVE_GETPW_DECLS */
63 #if !defined (savestring)
64 #define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
65 #endif /* !savestring */
68 # if defined (__STDC__)
69 # define NULL ((void *) 0)
72 # endif /* !__STDC__ */
75 /* If being compiled as part of bash, these will be satisfied from
76 variables.o. If being compiled as part of readline, they will
77 be satisfied from shell.o. */
78 extern char *sh_get_home_dir
PARAMS((void));
79 extern char *sh_get_env_value
PARAMS((const char *));
81 /* The default value of tilde_additional_prefixes. This is set to
82 whitespace preceding a tilde so that simple programs which do not
83 perform any word separation get desired behaviour. */
84 static const char *default_prefixes
[] =
85 { " ~", "\t~", (const char *)NULL
};
87 /* The default value of tilde_additional_suffixes. This is set to
88 whitespace or newline so that simple programs which do not
89 perform any word separation get desired behaviour. */
90 static const char *default_suffixes
[] =
91 { " ", "\n", (const char *)NULL
};
93 /* If non-null, this contains the address of a function that the application
94 wants called before trying the standard tilde expansions. The function
95 is called with the text sans tilde, and returns a malloc()'ed string
96 which is the expansion, or a NULL pointer if the expansion fails. */
97 tilde_hook_func_t
*tilde_expansion_preexpansion_hook
= (tilde_hook_func_t
*)NULL
;
99 /* If non-null, this contains the address of a function to call if the
100 standard meaning for expanding a tilde fails. The function is called
101 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
102 which is the expansion, or a NULL pointer if there is no expansion. */
103 tilde_hook_func_t
*tilde_expansion_failure_hook
= (tilde_hook_func_t
*)NULL
;
105 /* When non-null, this is a NULL terminated array of strings which
106 are duplicates for a tilde prefix. Bash uses this to expand
108 char **tilde_additional_prefixes
= (char **)default_prefixes
;
110 /* When non-null, this is a NULL terminated array of strings which match
111 the end of a username, instead of just "/". Bash sets this to
113 char **tilde_additional_suffixes
= (char **)default_suffixes
;
115 static int tilde_find_prefix
PARAMS((const char *, int *));
116 static int tilde_find_suffix
PARAMS((const char *));
117 static char *isolate_tilde_prefix
PARAMS((const char *, int *));
118 static char *glue_prefix_and_suffix
PARAMS((char *, const char *, int));
120 /* Find the start of a tilde expansion in STRING, and return the index of
121 the tilde which starts the expansion. Place the length of the text
122 which identified this tilde starter in LEN, excluding the tilde itself. */
124 tilde_find_prefix (string
, len
)
128 register int i
, j
, string_len
;
129 register char **prefixes
;
131 prefixes
= tilde_additional_prefixes
;
133 string_len
= strlen (string
);
136 if (*string
== '\0' || *string
== '~')
141 for (i
= 0; i
< string_len
; i
++)
143 for (j
= 0; prefixes
[j
]; j
++)
145 if (strncmp (string
+ i
, prefixes
[j
], strlen (prefixes
[j
])) == 0)
147 *len
= strlen (prefixes
[j
]) - 1;
156 /* Find the end of a tilde expansion in STRING, and return the index of
157 the character which ends the tilde definition. */
159 tilde_find_suffix (string
)
162 register int i
, j
, string_len
;
163 register char **suffixes
;
165 suffixes
= tilde_additional_suffixes
;
166 string_len
= strlen (string
);
168 for (i
= 0; i
< string_len
; i
++)
170 #if defined (__MSDOS__)
171 if (string
[i
] == '/' || string
[i
] == '\\' /* || !string[i] */)
173 if (string
[i
] == '/' /* || !string[i] */)
177 for (j
= 0; suffixes
&& suffixes
[j
]; j
++)
179 if (strncmp (string
+ i
, suffixes
[j
], strlen (suffixes
[j
])) == 0)
186 /* Return a new string which is the result of tilde expanding STRING. */
188 tilde_expand (string
)
192 int result_size
, result_index
;
194 result_index
= result_size
= 0;
195 if (result
= strchr (string
, '~'))
196 result
= (char *)xmalloc (result_size
= (strlen (string
) + 16));
198 result
= (char *)xmalloc (result_size
= (strlen (string
) + 1));
200 /* Scan through STRING expanding tildes as we come to them. */
203 register int start
, end
;
204 char *tilde_word
, *expansion
;
207 /* Make START point to the tilde which starts the expansion. */
208 start
= tilde_find_prefix (string
, &len
);
210 /* Copy the skipped text into the result. */
211 if ((result_index
+ start
+ 1) > result_size
)
212 result
= (char *)xrealloc (result
, 1 + (result_size
+= (start
+ 20)));
214 strncpy (result
+ result_index
, string
, start
);
215 result_index
+= start
;
217 /* Advance STRING to the starting tilde. */
220 /* Make END be the index of one after the last character of the
222 end
= tilde_find_suffix (string
);
224 /* If both START and END are zero, we are all done. */
228 /* Expand the entire tilde word, and copy it into RESULT. */
229 tilde_word
= (char *)xmalloc (1 + end
);
230 strncpy (tilde_word
, string
, end
);
231 tilde_word
[end
] = '\0';
234 expansion
= tilde_expand_word (tilde_word
);
237 len
= strlen (expansion
);
239 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
240 $HOME for `user' is /. On cygwin, // denotes a network drive. */
241 if (len
> 1 || *expansion
!= '/' || *string
!= '/')
244 if ((result_index
+ len
+ 1) > result_size
)
245 result
= (char *)xrealloc (result
, 1 + (result_size
+= (len
+ 20)));
247 strcpy (result
+ result_index
, expansion
);
253 result
[result_index
] = '\0';
258 /* Take FNAME and return the tilde prefix we want expanded. If LENP is
259 non-null, the index of the end of the prefix into FNAME is returned in
260 the location it points to. */
262 isolate_tilde_prefix (fname
, lenp
)
269 ret
= (char *)xmalloc (strlen (fname
));
270 #if defined (__MSDOS__)
271 for (i
= 1; fname
[i
] && fname
[i
] != '/' && fname
[i
] != '\\'; i
++)
273 for (i
= 1; fname
[i
] && fname
[i
] != '/'; i
++)
275 ret
[i
- 1] = fname
[i
];
282 /* Return a string that is PREFIX concatenated with SUFFIX starting at
285 glue_prefix_and_suffix (prefix
, suffix
, suffind
)
293 plen
= (prefix
&& *prefix
) ? strlen (prefix
) : 0;
294 slen
= strlen (suffix
+ suffind
);
295 ret
= (char *)xmalloc (plen
+ slen
+ 1);
297 strcpy (ret
, prefix
);
298 strcpy (ret
+ plen
, suffix
+ suffind
);
302 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
303 tilde. If there is no expansion, call tilde_expansion_failure_hook.
304 This always returns a newly-allocated string, never static storage. */
306 tilde_expand_word (filename
)
307 const char *filename
;
309 char *dirname
, *expansion
, *username
;
311 struct passwd
*user_entry
;
314 return ((char *)NULL
);
316 if (*filename
!= '~')
317 return (savestring (filename
));
319 /* A leading `~/' or a bare `~' is *always* translated to the value of
320 $HOME or the home directory of the current user, regardless of any
321 preexpansion hook. */
322 if (filename
[1] == '\0' || filename
[1] == '/')
324 /* Prefix $HOME to the rest of the string. */
325 expansion
= sh_get_env_value ("HOME");
327 /* If there is no HOME variable, look up the directory in
328 the password database. */
330 expansion
= sh_get_home_dir ();
332 return (glue_prefix_and_suffix (expansion
, filename
, 1));
335 username
= isolate_tilde_prefix (filename
, &user_len
);
337 if (tilde_expansion_preexpansion_hook
)
339 expansion
= (*tilde_expansion_preexpansion_hook
) (username
);
342 dirname
= glue_prefix_and_suffix (expansion
, filename
, user_len
);
349 /* No preexpansion hook, or the preexpansion hook failed. Look in the
350 password database. */
351 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
);
393 char *result
, line
[512];
398 printf ("~expand: ");
402 strcpy (line
, "done");
404 if ((strcmp (line
, "done") == 0) ||
405 (strcmp (line
, "quit") == 0) ||
406 (strcmp (line
, "exit") == 0))
412 result
= tilde_expand (line
);
413 printf (" --> %s\n", result
);
419 static void memory_error_and_abort ();
425 void *temp
= (char *)malloc (bytes
);
428 memory_error_and_abort ();
433 xrealloc (pointer
, bytes
)
440 temp
= malloc (bytes
);
442 temp
= realloc (pointer
, bytes
);
445 memory_error_and_abort ();
451 memory_error_and_abort ()
453 fprintf (stderr
, "readline: out of virtual memory\n");
459 * compile-command: "gcc -g -DTEST -o tilde tilde.c"