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)
23
# include "config_readline.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>
46
#if defined (HAVE_PWD_H)
52
#if defined (TEST) || defined (STATIC_MALLOC)
53
static void *xmalloc (), *xrealloc ();
56
#endif /* TEST || STATIC_MALLOC */
58
#if !defined (HAVE_GETPW_DECLS)
59
# if defined (HAVE_GETPWUID)
60
extern struct passwd *getpwuid PARAMS((uid_t));
62
# if defined (HAVE_GETPWNAM)
63
extern struct passwd *getpwnam PARAMS((const char *));
65
#endif /* !HAVE_GETPW_DECLS */
67
#if !defined (savestring)
68
#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
69
#endif /* !savestring */
72
# if defined (__STDC__)
73
# define NULL ((void *) 0)
76
# endif /* !__STDC__ */
79
/* If being compiled as part of bash, these will be satisfied from
80
variables.o. If being compiled as part of readline, they will
81
be satisfied from shell.o. */
82
extern char *sh_get_home_dir PARAMS((void));
83
extern char *sh_get_env_value PARAMS((const char *));
85
/* The default value of tilde_additional_prefixes. This is set to
86
whitespace preceding a tilde so that simple programs which do not
87
perform any word separation get desired behaviour. */
88
static const char *default_prefixes[] =
89
{ " ~", "\t~", (const char *)NULL };
91
/* The default value of tilde_additional_suffixes. This is set to
92
whitespace or newline so that simple programs which do not
93
perform any word separation get desired behaviour. */
94
static const char *default_suffixes[] =
95
{ " ", "\n", (const char *)NULL };
97
/* If non-null, this contains the address of a function that the application
98
wants called before trying the standard tilde expansions. The function
99
is called with the text sans tilde, and returns a malloc()'ed string
100
which is the expansion, or a NULL pointer if the expansion fails. */
101
tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
103
/* If non-null, this contains the address of a function to call if the
104
standard meaning for expanding a tilde fails. The function is called
105
with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
106
which is the expansion, or a NULL pointer if there is no expansion. */
107
tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
109
/* When non-null, this is a NULL terminated array of strings which
110
are duplicates for a tilde prefix. Bash uses this to expand
112
char **tilde_additional_prefixes = (char **)default_prefixes;
114
/* When non-null, this is a NULL terminated array of strings which match
115
the end of a username, instead of just "/". Bash sets this to
117
char **tilde_additional_suffixes = (char **)default_suffixes;
119
static int tilde_find_prefix PARAMS((const char *, int *));
120
static int tilde_find_suffix PARAMS((const char *));
121
static char *isolate_tilde_prefix PARAMS((const char *, int *));
122
static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
124
/* Find the start of a tilde expansion in STRING, and return the index of
125
the tilde which starts the expansion. Place the length of the text
126
which identified this tilde starter in LEN, excluding the tilde itself. */
128
tilde_find_prefix (string, len)
132
register int i, j, string_len;
133
register char **prefixes;
135
prefixes = tilde_additional_prefixes;
137
string_len = strlen (string);
140
if (*string == '\0' || *string == '~')
145
for (i = 0; i < string_len; i++)
147
for (j = 0; prefixes[j]; j++)
149
if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
151
*len = strlen (prefixes[j]) - 1;
160
/* Find the end of a tilde expansion in STRING, and return the index of
161
the character which ends the tilde definition. */
163
tilde_find_suffix (string)
166
register int i, j, string_len;
167
register char **suffixes;
169
suffixes = tilde_additional_suffixes;
170
string_len = strlen (string);
172
for (i = 0; i < string_len; i++)
174
#if defined (__MSDOS__)
175
if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
177
if (string[i] == '/' /* || !string[i] */)
181
for (j = 0; suffixes && suffixes[j]; j++)
183
if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
190
/* Return a new string which is the result of tilde expanding STRING. */
192
tilde_expand (string)
196
int result_size, result_index;
198
result_index = result_size = 0;
199
if ((result = strchr (string, '~')))
200
result = (char *)xmalloc (result_size = (strlen (string) + 16));
202
result = (char *)xmalloc (result_size = (strlen (string) + 1));
204
/* Scan through STRING expanding tildes as we come to them. */
207
register int start, end;
208
char *tilde_word, *expansion;
211
/* Make START point to the tilde which starts the expansion. */
212
start = tilde_find_prefix (string, &len);
214
/* Copy the skipped text into the result. */
215
if ((result_index + start + 1) > result_size)
216
result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
218
strncpy (result + result_index, string, start);
219
result_index += start;
221
/* Advance STRING to the starting tilde. */
224
/* Make END be the index of one after the last character of the
226
end = tilde_find_suffix (string);
228
/* If both START and END are zero, we are all done. */
232
/* Expand the entire tilde word, and copy it into RESULT. */
233
tilde_word = (char *)xmalloc (1 + end);
234
strncpy (tilde_word, string, end);
235
tilde_word[end] = '\0';
238
expansion = tilde_expand_word (tilde_word);
241
len = strlen (expansion);
243
/* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
244
$HOME for `user' is /. On cygwin, // denotes a network drive. */
245
if (len > 1 || *expansion != '/' || *string != '/')
248
if ((result_index + len + 1) > result_size)
249
result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
251
strcpy (result + result_index, expansion);
257
result[result_index] = '\0';
262
/* Take FNAME and return the tilde prefix we want expanded. If LENP is
263
non-null, the index of the end of the prefix into FNAME is returned in
264
the location it points to. */
266
isolate_tilde_prefix (fname, lenp)
273
ret = (char *)xmalloc (strlen (fname));
274
#if defined (__MSDOS__)
275
for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
277
for (i = 1; fname[i] && fname[i] != '/'; i++)
279
ret[i - 1] = fname[i];
287
/* Public function to scan a string (FNAME) beginning with a tilde and find
288
the portion of the string that should be passed to the tilde expansion
289
function. Right now, it just calls tilde_find_suffix and allocates new
290
memory, but it can be expanded to do different things later. */
292
tilde_find_word (fname, flags, lenp)
299
x = tilde_find_suffix (fname);
302
r = savestring (fname);
308
r = (char *)xmalloc (1 + x);
309
strncpy (r, fname, x);
319
/* Return a string that is PREFIX concatenated with SUFFIX starting at
322
glue_prefix_and_suffix (prefix, suffix, suffind)
330
plen = (prefix && *prefix) ? strlen (prefix) : 0;
331
slen = strlen (suffix + suffind);
332
ret = (char *)xmalloc (plen + slen + 1);
334
strcpy (ret, prefix);
335
strcpy (ret + plen, suffix + suffind);
339
/* Do the work of tilde expansion on FILENAME. FILENAME starts with a
340
tilde. If there is no expansion, call tilde_expansion_failure_hook.
341
This always returns a newly-allocated string, never static storage. */
343
tilde_expand_word (filename)
344
const char *filename;
346
char *dirname, *expansion, *username;
348
struct passwd *user_entry;
351
return ((char *)NULL);
353
if (*filename != '~')
354
return (savestring (filename));
356
/* A leading `~/' or a bare `~' is *always* translated to the value of
357
$HOME or the home directory of the current user, regardless of any
358
preexpansion hook. */
359
if (filename[1] == '\0' || filename[1] == '/')
361
/* Prefix $HOME to the rest of the string. */
362
expansion = sh_get_env_value ("HOME");
364
/* If there is no HOME variable, look up the directory in
365
the password database. */
367
expansion = sh_get_home_dir ();
369
return (glue_prefix_and_suffix (expansion, filename, 1));
372
username = isolate_tilde_prefix (filename, &user_len);
374
if (tilde_expansion_preexpansion_hook)
376
expansion = (*tilde_expansion_preexpansion_hook) (username);
379
dirname = glue_prefix_and_suffix (expansion, filename, user_len);
386
/* No preexpansion hook, or the preexpansion hook failed. Look in the
387
password database. */
388
dirname = (char *)NULL;
389
#if defined (HAVE_GETPWNAM)
390
user_entry = getpwnam (username);
396
/* If the calling program has a special syntax for expanding tildes,
397
and we couldn't find a standard expansion, then let them try. */
398
if (tilde_expansion_failure_hook)
400
expansion = (*tilde_expansion_failure_hook) (username);
403
dirname = glue_prefix_and_suffix (expansion, filename, user_len);
407
/* If we don't have a failure hook, or if the failure hook did not
408
expand the tilde, return a copy of what we were passed. */
410
dirname = savestring (filename);
412
#if defined (HAVE_GETPWENT)
414
dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
418
#if defined (HAVE_GETPWENT)
433
char *result, line[512];
438
printf ("~expand: ");
442
strcpy (line, "done");
444
if ((strcmp (line, "done") == 0) ||
445
(strcmp (line, "quit") == 0) ||
446
(strcmp (line, "exit") == 0))
452
result = tilde_expand (line);
453
printf (" --> %s\n", result);
459
static void memory_error_and_abort ();
465
void *temp = (char *)malloc (bytes);
468
memory_error_and_abort ();
473
xrealloc (pointer, bytes)
480
temp = malloc (bytes);
482
temp = realloc (pointer, bytes);
485
memory_error_and_abort ();
491
memory_error_and_abort ()
493
fprintf (stderr, "readline: out of virtual memory\n");
499
* compile-command: "gcc -g -DTEST -o tilde tilde.c"