]> git.ipfire.org Git - thirdparty/bash.git/blame - lib/readline/tilde.c
Bash-4.2 distribution sources and documentation
[thirdparty/bash.git] / lib / readline / tilde.c
CommitLineData
726f6388
JA
1/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
2
3185942a 3/* Copyright (C) 1988-2009 Free Software Foundation, Inc.
726f6388 4
3185942a
JA
5 This file is part of the GNU Readline Library (Readline), a library
6 for reading lines of text with interactive input and history editing.
726f6388 7
3185942a
JA
8 Readline is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
726f6388 12
3185942a
JA
13 Readline is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
726f6388
JA
17
18 You should have received a copy of the GNU General Public License
3185942a
JA
19 along with Readline. If not, see <http://www.gnu.org/licenses/>.
20*/
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 45#include <sys/types.h>
95732b49 46#if defined (HAVE_PWD_H)
726f6388 47#include <pwd.h>
95732b49 48#endif
726f6388 49
ccc6cda3
JA
50#include "tilde.h"
51
bb70624e 52#if defined (TEST) || defined (STATIC_MALLOC)
f73dda09 53static void *xmalloc (), *xrealloc ();
bb70624e 54#else
f73dda09 55# include "xmalloc.h"
bb70624e
JA
56#endif /* TEST || STATIC_MALLOC */
57
ccc6cda3 58#if !defined (HAVE_GETPW_DECLS)
95732b49 59# if defined (HAVE_GETPWUID)
f73dda09 60extern struct passwd *getpwuid PARAMS((uid_t));
95732b49
JA
61# endif
62# if defined (HAVE_GETPWNAM)
f73dda09 63extern struct passwd *getpwnam PARAMS((const char *));
95732b49 64# endif
ccc6cda3 65#endif /* !HAVE_GETPW_DECLS */
726f6388
JA
66
67#if !defined (savestring)
f73dda09 68#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
726f6388
JA
69#endif /* !savestring */
70
71#if !defined (NULL)
72# if defined (__STDC__)
73# define NULL ((void *) 0)
74# else
75# define NULL 0x0
76# endif /* !__STDC__ */
77#endif /* !NULL */
78
b72432fd
JA
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. */
f73dda09
JA
82extern char *sh_get_home_dir PARAMS((void));
83extern char *sh_get_env_value PARAMS((const char *));
b72432fd 84
726f6388
JA
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. */
28ef6c31
JA
88static const char *default_prefixes[] =
89 { " ~", "\t~", (const char *)NULL };
726f6388
JA
90
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. */
28ef6c31
JA
94static const char *default_suffixes[] =
95 { " ", "\n", (const char *)NULL };
726f6388 96
d166f048
JA
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. */
28ef6c31 101tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
d166f048 102
726f6388
JA
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. */
28ef6c31 107tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
726f6388
JA
108
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
111 `=~' and `:~'. */
28ef6c31 112char **tilde_additional_prefixes = (char **)default_prefixes;
726f6388
JA
113
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
116 `:' and `=~'. */
28ef6c31 117char **tilde_additional_suffixes = (char **)default_suffixes;
726f6388 118
f73dda09
JA
119static int tilde_find_prefix PARAMS((const char *, int *));
120static int tilde_find_suffix PARAMS((const char *));
121static char *isolate_tilde_prefix PARAMS((const char *, int *));
122static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
123
726f6388
JA
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. */
127static int
128tilde_find_prefix (string, len)
f73dda09 129 const char *string;
726f6388
JA
130 int *len;
131{
132 register int i, j, string_len;
bb70624e
JA
133 register char **prefixes;
134
135 prefixes = tilde_additional_prefixes;
726f6388
JA
136
137 string_len = strlen (string);
138 *len = 0;
139
d166f048 140 if (*string == '\0' || *string == '~')
726f6388
JA
141 return (0);
142
143 if (prefixes)
144 {
145 for (i = 0; i < string_len; i++)
146 {
147 for (j = 0; prefixes[j]; j++)
148 {
149 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
150 {
151 *len = strlen (prefixes[j]) - 1;
152 return (i + *len);
153 }
154 }
155 }
156 }
157 return (string_len);
158}
159
160/* Find the end of a tilde expansion in STRING, and return the index of
161 the character which ends the tilde definition. */
162static int
163tilde_find_suffix (string)
f73dda09 164 const char *string;
726f6388
JA
165{
166 register int i, j, string_len;
d166f048 167 register char **suffixes;
726f6388 168
d166f048 169 suffixes = tilde_additional_suffixes;
726f6388
JA
170 string_len = strlen (string);
171
172 for (i = 0; i < string_len; i++)
173 {
bb70624e
JA
174#if defined (__MSDOS__)
175 if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
176#else
d166f048 177 if (string[i] == '/' /* || !string[i] */)
bb70624e 178#endif
726f6388
JA
179 break;
180
181 for (j = 0; suffixes && suffixes[j]; j++)
182 {
183 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
184 return (i);
185 }
186 }
187 return (i);
188}
189
190/* Return a new string which is the result of tilde expanding STRING. */
191char *
192tilde_expand (string)
28ef6c31 193 const char *string;
726f6388 194{
d166f048 195 char *result;
726f6388
JA
196 int result_size, result_index;
197
d166f048
JA
198 result_index = result_size = 0;
199 if (result = strchr (string, '~'))
f73dda09 200 result = (char *)xmalloc (result_size = (strlen (string) + 16));
d166f048 201 else
f73dda09 202 result = (char *)xmalloc (result_size = (strlen (string) + 1));
726f6388
JA
203
204 /* Scan through STRING expanding tildes as we come to them. */
205 while (1)
206 {
207 register int start, end;
208 char *tilde_word, *expansion;
209 int len;
210
211 /* Make START point to the tilde which starts the expansion. */
212 start = tilde_find_prefix (string, &len);
213
214 /* Copy the skipped text into the result. */
215 if ((result_index + start + 1) > result_size)
f73dda09 216 result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
726f6388
JA
217
218 strncpy (result + result_index, string, start);
219 result_index += start;
220
221 /* Advance STRING to the starting tilde. */
222 string += start;
223
224 /* Make END be the index of one after the last character of the
225 username. */
226 end = tilde_find_suffix (string);
227
228 /* If both START and END are zero, we are all done. */
229 if (!start && !end)
230 break;
231
232 /* Expand the entire tilde word, and copy it into RESULT. */
f73dda09 233 tilde_word = (char *)xmalloc (1 + end);
726f6388
JA
234 strncpy (tilde_word, string, end);
235 tilde_word[end] = '\0';
236 string += end;
237
238 expansion = tilde_expand_word (tilde_word);
3185942a 239 xfree (tilde_word);
726f6388
JA
240
241 len = strlen (expansion);
28ef6c31 242#ifdef __CYGWIN__
bb70624e 243 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
28ef6c31 244 $HOME for `user' is /. On cygwin, // denotes a network drive. */
bb70624e
JA
245 if (len > 1 || *expansion != '/' || *string != '/')
246#endif
247 {
248 if ((result_index + len + 1) > result_size)
f73dda09 249 result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
726f6388 250
bb70624e
JA
251 strcpy (result + result_index, expansion);
252 result_index += len;
253 }
3185942a 254 xfree (expansion);
726f6388
JA
255 }
256
257 result[result_index] = '\0';
258
259 return (result);
260}
261
d166f048
JA
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. */
265static char *
266isolate_tilde_prefix (fname, lenp)
f73dda09 267 const char *fname;
d166f048
JA
268 int *lenp;
269{
270 char *ret;
271 int i;
272
f73dda09 273 ret = (char *)xmalloc (strlen (fname));
bb70624e
JA
274#if defined (__MSDOS__)
275 for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
276#else
d166f048 277 for (i = 1; fname[i] && fname[i] != '/'; i++)
bb70624e 278#endif
d166f048
JA
279 ret[i - 1] = fname[i];
280 ret[i - 1] = '\0';
281 if (lenp)
282 *lenp = i;
283 return ret;
284}
285
95732b49
JA
286#if 0
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. */
291char *
292tilde_find_word (fname, flags, lenp)
293 const char *fname;
294 int flags, *lenp;
295{
296 int x;
297 char *r;
298
299 x = tilde_find_suffix (fname);
300 if (x == 0)
301 {
302 r = savestring (fname);
303 if (lenp)
304 *lenp = 0;
305 }
306 else
307 {
308 r = (char *)xmalloc (1 + x);
309 strncpy (r, fname, x);
310 r[x] = '\0';
311 if (lenp)
312 *lenp = x;
313 }
314
315 return r;
316}
317#endif
318
d166f048
JA
319/* Return a string that is PREFIX concatenated with SUFFIX starting at
320 SUFFIND. */
321static char *
322glue_prefix_and_suffix (prefix, suffix, suffind)
f73dda09
JA
323 char *prefix;
324 const char *suffix;
d166f048
JA
325 int suffind;
326{
327 char *ret;
328 int plen, slen;
329
330 plen = (prefix && *prefix) ? strlen (prefix) : 0;
331 slen = strlen (suffix + suffind);
f73dda09 332 ret = (char *)xmalloc (plen + slen + 1);
bb70624e 333 if (plen)
d166f048
JA
334 strcpy (ret, prefix);
335 strcpy (ret + plen, suffix + suffind);
336 return ret;
337}
338
726f6388 339/* Do the work of tilde expansion on FILENAME. FILENAME starts with a
d166f048
JA
340 tilde. If there is no expansion, call tilde_expansion_failure_hook.
341 This always returns a newly-allocated string, never static storage. */
726f6388
JA
342char *
343tilde_expand_word (filename)
28ef6c31 344 const char *filename;
726f6388 345{
d166f048
JA
346 char *dirname, *expansion, *username;
347 int user_len;
348 struct passwd *user_entry;
726f6388 349
d166f048 350 if (filename == 0)
ccc6cda3 351 return ((char *)NULL);
726f6388 352
d166f048
JA
353 if (*filename != '~')
354 return (savestring (filename));
ccc6cda3 355
d166f048
JA
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] == '/')
726f6388 360 {
d166f048 361 /* Prefix $HOME to the rest of the string. */
28ef6c31 362 expansion = sh_get_env_value ("HOME");
ccc6cda3
JA
363
364 /* If there is no HOME variable, look up the directory in
365 the password database. */
d166f048 366 if (expansion == 0)
28ef6c31 367 expansion = sh_get_home_dir ();
726f6388 368
d166f048 369 return (glue_prefix_and_suffix (expansion, filename, 1));
ccc6cda3 370 }
726f6388 371
d166f048 372 username = isolate_tilde_prefix (filename, &user_len);
ccc6cda3 373
d166f048
JA
374 if (tilde_expansion_preexpansion_hook)
375 {
376 expansion = (*tilde_expansion_preexpansion_hook) (username);
377 if (expansion)
726f6388 378 {
d166f048 379 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
3185942a 380 xfree (username);
495aee44 381 xfree (expansion);
d166f048 382 return (dirname);
726f6388 383 }
d166f048
JA
384 }
385
386 /* No preexpansion hook, or the preexpansion hook failed. Look in the
387 password database. */
388 dirname = (char *)NULL;
95732b49 389#if defined (HAVE_GETPWNAM)
d166f048 390 user_entry = getpwnam (username);
95732b49
JA
391#else
392 user_entry = 0;
393#endif
d166f048
JA
394 if (user_entry == 0)
395 {
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)
ccc6cda3 399 {
d166f048
JA
400 expansion = (*tilde_expansion_failure_hook) (username);
401 if (expansion)
402 {
403 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
495aee44 404 xfree (expansion);
d166f048 405 }
ccc6cda3 406 }
d166f048
JA
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. */
409 if (dirname == 0)
410 dirname = savestring (filename);
411 }
0628567a 412#if defined (HAVE_GETPWENT)
d166f048 413 else
0628567a
JA
414 dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
415#endif
416
3185942a 417 xfree (username);
95732b49 418#if defined (HAVE_GETPWENT)
d166f048 419 endpwent ();
95732b49 420#endif
726f6388
JA
421 return (dirname);
422}
423
424\f
425#if defined (TEST)
426#undef NULL
427#include <stdio.h>
428
429main (argc, argv)
430 int argc;
431 char **argv;
432{
433 char *result, line[512];
434 int done = 0;
435
436 while (!done)
437 {
438 printf ("~expand: ");
439 fflush (stdout);
440
441 if (!gets (line))
442 strcpy (line, "done");
443
444 if ((strcmp (line, "done") == 0) ||
445 (strcmp (line, "quit") == 0) ||
446 (strcmp (line, "exit") == 0))
447 {
448 done = 1;
449 break;
450 }
451
452 result = tilde_expand (line);
453 printf (" --> %s\n", result);
454 free (result);
455 }
456 exit (0);
457}
458
459static void memory_error_and_abort ();
460
f73dda09 461static void *
726f6388 462xmalloc (bytes)
f73dda09 463 size_t bytes;
726f6388 464{
f73dda09 465 void *temp = (char *)malloc (bytes);
726f6388
JA
466
467 if (!temp)
468 memory_error_and_abort ();
469 return (temp);
470}
471
f73dda09 472static void *
726f6388 473xrealloc (pointer, bytes)
f73dda09 474 void *pointer;
726f6388
JA
475 int bytes;
476{
f73dda09 477 void *temp;
726f6388
JA
478
479 if (!pointer)
f73dda09 480 temp = malloc (bytes);
726f6388 481 else
f73dda09 482 temp = realloc (pointer, bytes);
726f6388
JA
483
484 if (!temp)
485 memory_error_and_abort ();
486
487 return (temp);
488}
489
490static void
491memory_error_and_abort ()
492{
ccc6cda3 493 fprintf (stderr, "readline: out of virtual memory\n");
726f6388
JA
494 abort ();
495}
496
497/*
498 * Local variables:
499 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
500 * end:
501 */
502#endif /* TEST */