Import readline 7.0 (patch 5)
[deliverable/binutils-gdb.git] / readline / tilde.c
... / ...
CommitLineData
1/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
2
3/* Copyright (C) 1988-2009 Free Software Foundation, Inc.
4
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.
7
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.
12
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.
17
18 You should have received a copy of the GNU General Public License
19 along with Readline. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#if defined (HAVE_CONFIG_H)
23# include <config.h>
24#endif
25
26#if defined (HAVE_UNISTD_H)
27# ifdef _MINIX
28# include <sys/types.h>
29# endif
30# include <unistd.h>
31#endif
32
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
45#include <sys/types.h>
46#if defined (HAVE_PWD_H)
47#include <pwd.h>
48#endif
49
50#include "tilde.h"
51
52#if defined (TEST) || defined (STATIC_MALLOC)
53static void *xmalloc (), *xrealloc ();
54#else
55# include "xmalloc.h"
56#endif /* TEST || STATIC_MALLOC */
57
58#if !defined (HAVE_GETPW_DECLS)
59# if defined (HAVE_GETPWUID)
60extern struct passwd *getpwuid PARAMS((uid_t));
61# endif
62# if defined (HAVE_GETPWNAM)
63extern struct passwd *getpwnam PARAMS((const char *));
64# endif
65#endif /* !HAVE_GETPW_DECLS */
66
67#if !defined (savestring)
68#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
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
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. */
82extern char *sh_get_home_dir PARAMS((void));
83extern char *sh_get_env_value PARAMS((const char *));
84
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. */
88static const char *default_prefixes[] =
89 { " ~", "\t~", (const char *)NULL };
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. */
94static const char *default_suffixes[] =
95 { " ", "\n", (const char *)NULL };
96
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. */
101tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
102
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. */
107tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
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 `:~'. */
112char **tilde_additional_prefixes = (char **)default_prefixes;
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 `=~'. */
117char **tilde_additional_suffixes = (char **)default_suffixes;
118
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
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)
129 const char *string;
130 int *len;
131{
132 register int i, j, string_len;
133 register char **prefixes;
134
135 prefixes = tilde_additional_prefixes;
136
137 string_len = strlen (string);
138 *len = 0;
139
140 if (*string == '\0' || *string == '~')
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)
164 const char *string;
165{
166 register int i, j, string_len;
167 register char **suffixes;
168
169 suffixes = tilde_additional_suffixes;
170 string_len = strlen (string);
171
172 for (i = 0; i < string_len; i++)
173 {
174#if defined (__MSDOS__)
175 if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
176#else
177 if (string[i] == '/' /* || !string[i] */)
178#endif
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)
193 const char *string;
194{
195 char *result;
196 int result_size, result_index;
197
198 result_index = result_size = 0;
199 if (result = strchr (string, '~'))
200 result = (char *)xmalloc (result_size = (strlen (string) + 16));
201 else
202 result = (char *)xmalloc (result_size = (strlen (string) + 1));
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)
216 result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
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. */
233 tilde_word = (char *)xmalloc (1 + end);
234 strncpy (tilde_word, string, end);
235 tilde_word[end] = '\0';
236 string += end;
237
238 expansion = tilde_expand_word (tilde_word);
239
240 if (expansion == 0)
241 expansion = tilde_word;
242 else
243 xfree (tilde_word);
244
245 len = strlen (expansion);
246#ifdef __CYGWIN__
247 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
248 $HOME for `user' is /. On cygwin, // denotes a network drive. */
249 if (len > 1 || *expansion != '/' || *string != '/')
250#endif
251 {
252 if ((result_index + len + 1) > result_size)
253 result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
254
255 strcpy (result + result_index, expansion);
256 result_index += len;
257 }
258 xfree (expansion);
259 }
260
261 result[result_index] = '\0';
262
263 return (result);
264}
265
266/* Take FNAME and return the tilde prefix we want expanded. If LENP is
267 non-null, the index of the end of the prefix into FNAME is returned in
268 the location it points to. */
269static char *
270isolate_tilde_prefix (fname, lenp)
271 const char *fname;
272 int *lenp;
273{
274 char *ret;
275 int i;
276
277 ret = (char *)xmalloc (strlen (fname));
278#if defined (__MSDOS__)
279 for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
280#else
281 for (i = 1; fname[i] && fname[i] != '/'; i++)
282#endif
283 ret[i - 1] = fname[i];
284 ret[i - 1] = '\0';
285 if (lenp)
286 *lenp = i;
287 return ret;
288}
289
290#if 0
291/* Public function to scan a string (FNAME) beginning with a tilde and find
292 the portion of the string that should be passed to the tilde expansion
293 function. Right now, it just calls tilde_find_suffix and allocates new
294 memory, but it can be expanded to do different things later. */
295char *
296tilde_find_word (fname, flags, lenp)
297 const char *fname;
298 int flags, *lenp;
299{
300 int x;
301 char *r;
302
303 x = tilde_find_suffix (fname);
304 if (x == 0)
305 {
306 r = savestring (fname);
307 if (lenp)
308 *lenp = 0;
309 }
310 else
311 {
312 r = (char *)xmalloc (1 + x);
313 strncpy (r, fname, x);
314 r[x] = '\0';
315 if (lenp)
316 *lenp = x;
317 }
318
319 return r;
320}
321#endif
322
323/* Return a string that is PREFIX concatenated with SUFFIX starting at
324 SUFFIND. */
325static char *
326glue_prefix_and_suffix (prefix, suffix, suffind)
327 char *prefix;
328 const char *suffix;
329 int suffind;
330{
331 char *ret;
332 int plen, slen;
333
334 plen = (prefix && *prefix) ? strlen (prefix) : 0;
335 slen = strlen (suffix + suffind);
336 ret = (char *)xmalloc (plen + slen + 1);
337 if (plen)
338 strcpy (ret, prefix);
339 strcpy (ret + plen, suffix + suffind);
340 return ret;
341}
342
343/* Do the work of tilde expansion on FILENAME. FILENAME starts with a
344 tilde. If there is no expansion, call tilde_expansion_failure_hook.
345 This always returns a newly-allocated string, never static storage. */
346char *
347tilde_expand_word (filename)
348 const char *filename;
349{
350 char *dirname, *expansion, *username;
351 int user_len;
352 struct passwd *user_entry;
353
354 if (filename == 0)
355 return ((char *)NULL);
356
357 if (*filename != '~')
358 return (savestring (filename));
359
360 /* A leading `~/' or a bare `~' is *always* translated to the value of
361 $HOME or the home directory of the current user, regardless of any
362 preexpansion hook. */
363 if (filename[1] == '\0' || filename[1] == '/')
364 {
365 /* Prefix $HOME to the rest of the string. */
366 expansion = sh_get_env_value ("HOME");
367#if defined (_WIN32)
368 if (expansion == 0)
369 expansion = sh_get_env_value ("APPDATA");
370#endif
371
372 /* If there is no HOME variable, look up the directory in
373 the password database. */
374 if (expansion == 0)
375 expansion = sh_get_home_dir ();
376
377 return (glue_prefix_and_suffix (expansion, filename, 1));
378 }
379
380 username = isolate_tilde_prefix (filename, &user_len);
381
382 if (tilde_expansion_preexpansion_hook)
383 {
384 expansion = (*tilde_expansion_preexpansion_hook) (username);
385 if (expansion)
386 {
387 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
388 xfree (username);
389 xfree (expansion);
390 return (dirname);
391 }
392 }
393
394 /* No preexpansion hook, or the preexpansion hook failed. Look in the
395 password database. */
396 dirname = (char *)NULL;
397#if defined (HAVE_GETPWNAM)
398 user_entry = getpwnam (username);
399#else
400 user_entry = 0;
401#endif
402 if (user_entry == 0)
403 {
404 /* If the calling program has a special syntax for expanding tildes,
405 and we couldn't find a standard expansion, then let them try. */
406 if (tilde_expansion_failure_hook)
407 {
408 expansion = (*tilde_expansion_failure_hook) (username);
409 if (expansion)
410 {
411 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
412 xfree (expansion);
413 }
414 }
415 /* If we don't have a failure hook, or if the failure hook did not
416 expand the tilde, return a copy of what we were passed. */
417 if (dirname == 0)
418 dirname = savestring (filename);
419 }
420#if defined (HAVE_GETPWENT)
421 else
422 dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
423#endif
424
425 xfree (username);
426#if defined (HAVE_GETPWENT)
427 endpwent ();
428#endif
429 return (dirname);
430}
431
432\f
433#if defined (TEST)
434#undef NULL
435#include <stdio.h>
436
437main (argc, argv)
438 int argc;
439 char **argv;
440{
441 char *result, line[512];
442 int done = 0;
443
444 while (!done)
445 {
446 printf ("~expand: ");
447 fflush (stdout);
448
449 if (!gets (line))
450 strcpy (line, "done");
451
452 if ((strcmp (line, "done") == 0) ||
453 (strcmp (line, "quit") == 0) ||
454 (strcmp (line, "exit") == 0))
455 {
456 done = 1;
457 break;
458 }
459
460 result = tilde_expand (line);
461 printf (" --> %s\n", result);
462 free (result);
463 }
464 exit (0);
465}
466
467static void memory_error_and_abort ();
468
469static void *
470xmalloc (bytes)
471 size_t bytes;
472{
473 void *temp = (char *)malloc (bytes);
474
475 if (!temp)
476 memory_error_and_abort ();
477 return (temp);
478}
479
480static void *
481xrealloc (pointer, bytes)
482 void *pointer;
483 int bytes;
484{
485 void *temp;
486
487 if (!pointer)
488 temp = malloc (bytes);
489 else
490 temp = realloc (pointer, bytes);
491
492 if (!temp)
493 memory_error_and_abort ();
494
495 return (temp);
496}
497
498static void
499memory_error_and_abort ()
500{
501 fprintf (stderr, "readline: out of virtual memory\n");
502 abort ();
503}
504
505/*
506 * Local variables:
507 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
508 * end:
509 */
510#endif /* TEST */
This page took 0.023908 seconds and 4 git commands to generate.