]> git.ipfire.org Git - thirdparty/bash.git/blame - lib/readline/tilde.c
Imported from ../bash-2.05.tar.gz.
[thirdparty/bash.git] / lib / readline / tilde.c
CommitLineData
726f6388
JA
1/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
2
3/* Copyright (C) 1988,1989 Free Software Foundation, Inc.
4
5 This file is part of GNU Readline, a library for reading lines
6 of text with interactive input and history editing.
7
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
bb70624e 10 Free Software Foundation; either version 2, or (at your option) any
726f6388
JA
11 later version.
12
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.
17
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
bb70624e 20 Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
726f6388 21
ccc6cda3
JA
22#if defined (HAVE_CONFIG_H)
23# include <config.h>
24#endif
25
d166f048 26#if defined (HAVE_UNISTD_H)
cce855bc
JA
27# ifdef _MINIX
28# include <sys/types.h>
29# endif
d166f048
JA
30# include <unistd.h>
31#endif
32
726f6388
JA
33#if defined (HAVE_STRING_H)
34# include <string.h>
35#else /* !HAVE_STRING_H */
36# include <strings.h>
37#endif /* !HAVE_STRING_H */
38
39#if defined (HAVE_STDLIB_H)
40# include <stdlib.h>
41#else
42# include "ansi_stdlib.h"
43#endif /* HAVE_STDLIB_H */
44
726f6388
JA
45#include <sys/types.h>
46#include <pwd.h>
47
ccc6cda3
JA
48#include "tilde.h"
49
bb70624e
JA
50#if defined (TEST) || defined (STATIC_MALLOC)
51static char *xmalloc (), *xrealloc ();
52#else
28ef6c31
JA
53extern char *xmalloc __P((int));
54extern char *xrealloc __P((void *, int));
bb70624e
JA
55#endif /* TEST || STATIC_MALLOC */
56
ccc6cda3 57#if !defined (HAVE_GETPW_DECLS)
28ef6c31
JA
58extern struct passwd *getpwuid __P((uid_t));
59extern struct passwd *getpwnam __P((const char *));
ccc6cda3 60#endif /* !HAVE_GETPW_DECLS */
726f6388
JA
61
62#if !defined (savestring)
726f6388
JA
63# ifndef strcpy
64extern char *strcpy ();
65# endif
66#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
67#endif /* !savestring */
68
69#if !defined (NULL)
70# if defined (__STDC__)
71# define NULL ((void *) 0)
72# else
73# define NULL 0x0
74# endif /* !__STDC__ */
75#endif /* !NULL */
76
b72432fd
JA
77/* If being compiled as part of bash, these will be satisfied from
78 variables.o. If being compiled as part of readline, they will
79 be satisfied from shell.o. */
28ef6c31
JA
80extern char *sh_get_home_dir __P((void));
81extern char *sh_get_env_value __P((const char *));
b72432fd 82
726f6388
JA
83/* The default value of tilde_additional_prefixes. This is set to
84 whitespace preceding a tilde so that simple programs which do not
85 perform any word separation get desired behaviour. */
28ef6c31
JA
86static const char *default_prefixes[] =
87 { " ~", "\t~", (const char *)NULL };
726f6388
JA
88
89/* The default value of tilde_additional_suffixes. This is set to
90 whitespace or newline so that simple programs which do not
91 perform any word separation get desired behaviour. */
28ef6c31
JA
92static const char *default_suffixes[] =
93 { " ", "\n", (const char *)NULL };
726f6388 94
d166f048
JA
95/* If non-null, this contains the address of a function that the application
96 wants called before trying the standard tilde expansions. The function
97 is called with the text sans tilde, and returns a malloc()'ed string
98 which is the expansion, or a NULL pointer if the expansion fails. */
28ef6c31 99tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
d166f048 100
726f6388
JA
101/* If non-null, this contains the address of a function to call if the
102 standard meaning for expanding a tilde fails. The function is called
103 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
104 which is the expansion, or a NULL pointer if there is no expansion. */
28ef6c31 105tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
726f6388
JA
106
107/* When non-null, this is a NULL terminated array of strings which
108 are duplicates for a tilde prefix. Bash uses this to expand
109 `=~' and `:~'. */
28ef6c31 110char **tilde_additional_prefixes = (char **)default_prefixes;
726f6388
JA
111
112/* When non-null, this is a NULL terminated array of strings which match
113 the end of a username, instead of just "/". Bash sets this to
114 `:' and `=~'. */
28ef6c31 115char **tilde_additional_suffixes = (char **)default_suffixes;
726f6388
JA
116
117/* Find the start of a tilde expansion in STRING, and return the index of
118 the tilde which starts the expansion. Place the length of the text
119 which identified this tilde starter in LEN, excluding the tilde itself. */
120static int
121tilde_find_prefix (string, len)
122 char *string;
123 int *len;
124{
125 register int i, j, string_len;
bb70624e
JA
126 register char **prefixes;
127
128 prefixes = tilde_additional_prefixes;
726f6388
JA
129
130 string_len = strlen (string);
131 *len = 0;
132
d166f048 133 if (*string == '\0' || *string == '~')
726f6388
JA
134 return (0);
135
136 if (prefixes)
137 {
138 for (i = 0; i < string_len; i++)
139 {
140 for (j = 0; prefixes[j]; j++)
141 {
142 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
143 {
144 *len = strlen (prefixes[j]) - 1;
145 return (i + *len);
146 }
147 }
148 }
149 }
150 return (string_len);
151}
152
153/* Find the end of a tilde expansion in STRING, and return the index of
154 the character which ends the tilde definition. */
155static int
156tilde_find_suffix (string)
157 char *string;
158{
159 register int i, j, string_len;
d166f048 160 register char **suffixes;
726f6388 161
d166f048 162 suffixes = tilde_additional_suffixes;
726f6388
JA
163 string_len = strlen (string);
164
165 for (i = 0; i < string_len; i++)
166 {
bb70624e
JA
167#if defined (__MSDOS__)
168 if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
169#else
d166f048 170 if (string[i] == '/' /* || !string[i] */)
bb70624e 171#endif
726f6388
JA
172 break;
173
174 for (j = 0; suffixes && suffixes[j]; j++)
175 {
176 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
177 return (i);
178 }
179 }
180 return (i);
181}
182
183/* Return a new string which is the result of tilde expanding STRING. */
184char *
185tilde_expand (string)
28ef6c31 186 const char *string;
726f6388 187{
d166f048 188 char *result;
726f6388
JA
189 int result_size, result_index;
190
d166f048
JA
191 result_index = result_size = 0;
192 if (result = strchr (string, '~'))
193 result = xmalloc (result_size = (strlen (string) + 16));
194 else
e8ce775d 195 result = xmalloc (result_size = (strlen (string) + 1));
726f6388
JA
196
197 /* Scan through STRING expanding tildes as we come to them. */
198 while (1)
199 {
200 register int start, end;
201 char *tilde_word, *expansion;
202 int len;
203
204 /* Make START point to the tilde which starts the expansion. */
205 start = tilde_find_prefix (string, &len);
206
207 /* Copy the skipped text into the result. */
208 if ((result_index + start + 1) > result_size)
ccc6cda3 209 result = xrealloc (result, 1 + (result_size += (start + 20)));
726f6388
JA
210
211 strncpy (result + result_index, string, start);
212 result_index += start;
213
214 /* Advance STRING to the starting tilde. */
215 string += start;
216
217 /* Make END be the index of one after the last character of the
218 username. */
219 end = tilde_find_suffix (string);
220
221 /* If both START and END are zero, we are all done. */
222 if (!start && !end)
223 break;
224
225 /* Expand the entire tilde word, and copy it into RESULT. */
ccc6cda3 226 tilde_word = xmalloc (1 + end);
726f6388
JA
227 strncpy (tilde_word, string, end);
228 tilde_word[end] = '\0';
229 string += end;
230
231 expansion = tilde_expand_word (tilde_word);
232 free (tilde_word);
233
234 len = strlen (expansion);
28ef6c31 235#ifdef __CYGWIN__
bb70624e 236 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
28ef6c31 237 $HOME for `user' is /. On cygwin, // denotes a network drive. */
bb70624e
JA
238 if (len > 1 || *expansion != '/' || *string != '/')
239#endif
240 {
241 if ((result_index + len + 1) > result_size)
242 result = xrealloc (result, 1 + (result_size += (len + 20)));
726f6388 243
bb70624e
JA
244 strcpy (result + result_index, expansion);
245 result_index += len;
246 }
726f6388
JA
247 free (expansion);
248 }
249
250 result[result_index] = '\0';
251
252 return (result);
253}
254
d166f048
JA
255/* Take FNAME and return the tilde prefix we want expanded. If LENP is
256 non-null, the index of the end of the prefix into FNAME is returned in
257 the location it points to. */
258static char *
259isolate_tilde_prefix (fname, lenp)
260 char *fname;
261 int *lenp;
262{
263 char *ret;
264 int i;
265
266 ret = xmalloc (strlen (fname));
bb70624e
JA
267#if defined (__MSDOS__)
268 for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
269#else
d166f048 270 for (i = 1; fname[i] && fname[i] != '/'; i++)
bb70624e 271#endif
d166f048
JA
272 ret[i - 1] = fname[i];
273 ret[i - 1] = '\0';
274 if (lenp)
275 *lenp = i;
276 return ret;
277}
278
279/* Return a string that is PREFIX concatenated with SUFFIX starting at
280 SUFFIND. */
281static char *
282glue_prefix_and_suffix (prefix, suffix, suffind)
283 char *prefix, *suffix;
284 int suffind;
285{
286 char *ret;
287 int plen, slen;
288
289 plen = (prefix && *prefix) ? strlen (prefix) : 0;
290 slen = strlen (suffix + suffind);
291 ret = xmalloc (plen + slen + 1);
bb70624e 292 if (plen)
d166f048
JA
293 strcpy (ret, prefix);
294 strcpy (ret + plen, suffix + suffind);
295 return ret;
296}
297
726f6388 298/* Do the work of tilde expansion on FILENAME. FILENAME starts with a
d166f048
JA
299 tilde. If there is no expansion, call tilde_expansion_failure_hook.
300 This always returns a newly-allocated string, never static storage. */
726f6388
JA
301char *
302tilde_expand_word (filename)
28ef6c31 303 const char *filename;
726f6388 304{
d166f048
JA
305 char *dirname, *expansion, *username;
306 int user_len;
307 struct passwd *user_entry;
726f6388 308
d166f048 309 if (filename == 0)
ccc6cda3 310 return ((char *)NULL);
726f6388 311
d166f048
JA
312 if (*filename != '~')
313 return (savestring (filename));
ccc6cda3 314
d166f048
JA
315 /* A leading `~/' or a bare `~' is *always* translated to the value of
316 $HOME or the home directory of the current user, regardless of any
317 preexpansion hook. */
318 if (filename[1] == '\0' || filename[1] == '/')
726f6388 319 {
d166f048 320 /* Prefix $HOME to the rest of the string. */
28ef6c31 321 expansion = sh_get_env_value ("HOME");
ccc6cda3
JA
322
323 /* If there is no HOME variable, look up the directory in
324 the password database. */
d166f048 325 if (expansion == 0)
28ef6c31 326 expansion = sh_get_home_dir ();
726f6388 327
d166f048 328 return (glue_prefix_and_suffix (expansion, filename, 1));
ccc6cda3 329 }
726f6388 330
d166f048 331 username = isolate_tilde_prefix (filename, &user_len);
ccc6cda3 332
d166f048
JA
333 if (tilde_expansion_preexpansion_hook)
334 {
335 expansion = (*tilde_expansion_preexpansion_hook) (username);
336 if (expansion)
726f6388 337 {
d166f048
JA
338 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
339 free (username);
340 free (expansion);
341 return (dirname);
726f6388 342 }
d166f048
JA
343 }
344
345 /* No preexpansion hook, or the preexpansion hook failed. Look in the
346 password database. */
347 dirname = (char *)NULL;
348 user_entry = getpwnam (username);
349 if (user_entry == 0)
350 {
351 /* If the calling program has a special syntax for expanding tildes,
352 and we couldn't find a standard expansion, then let them try. */
353 if (tilde_expansion_failure_hook)
ccc6cda3 354 {
d166f048
JA
355 expansion = (*tilde_expansion_failure_hook) (username);
356 if (expansion)
357 {
358 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
359 free (expansion);
360 }
ccc6cda3 361 }
ccc6cda3 362 free (username);
d166f048
JA
363 /* If we don't have a failure hook, or if the failure hook did not
364 expand the tilde, return a copy of what we were passed. */
365 if (dirname == 0)
366 dirname = savestring (filename);
367 }
368 else
369 {
370 free (username);
371 dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
726f6388 372 }
ccc6cda3 373
d166f048 374 endpwent ();
726f6388
JA
375 return (dirname);
376}
377
378\f
379#if defined (TEST)
380#undef NULL
381#include <stdio.h>
382
383main (argc, argv)
384 int argc;
385 char **argv;
386{
387 char *result, line[512];
388 int done = 0;
389
390 while (!done)
391 {
392 printf ("~expand: ");
393 fflush (stdout);
394
395 if (!gets (line))
396 strcpy (line, "done");
397
398 if ((strcmp (line, "done") == 0) ||
399 (strcmp (line, "quit") == 0) ||
400 (strcmp (line, "exit") == 0))
401 {
402 done = 1;
403 break;
404 }
405
406 result = tilde_expand (line);
407 printf (" --> %s\n", result);
408 free (result);
409 }
410 exit (0);
411}
412
413static void memory_error_and_abort ();
414
415static char *
416xmalloc (bytes)
417 int bytes;
418{
419 char *temp = (char *)malloc (bytes);
420
421 if (!temp)
422 memory_error_and_abort ();
423 return (temp);
424}
425
426static char *
427xrealloc (pointer, bytes)
428 char *pointer;
429 int bytes;
430{
431 char *temp;
432
433 if (!pointer)
434 temp = (char *)malloc (bytes);
435 else
436 temp = (char *)realloc (pointer, bytes);
437
438 if (!temp)
439 memory_error_and_abort ();
440
441 return (temp);
442}
443
444static void
445memory_error_and_abort ()
446{
ccc6cda3 447 fprintf (stderr, "readline: out of virtual memory\n");
726f6388
JA
448 abort ();
449}
450
451/*
452 * Local variables:
453 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
454 * end:
455 */
456#endif /* TEST */