* readline/aclocal.m4: Use AC_TRY_LINK to check for mbstate_t.
[deliverable/binutils-gdb.git] / readline / tilde.c
CommitLineData
d60d9f65
SS
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
1b17e766 10 Free Software Foundation; either version 2, or (at your option) any
d60d9f65
SS
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
1b17e766 20 Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
d60d9f65
SS
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>
430b7832 46#ifdef HAVE_PWD_H
d60d9f65 47#include <pwd.h>
430b7832 48#endif /* HAVE_PWD_H */
d60d9f65
SS
49
50#include "tilde.h"
51
1b17e766 52#if defined (TEST) || defined (STATIC_MALLOC)
9255ee31 53static void *xmalloc (), *xrealloc ();
1b17e766 54#else
9255ee31 55# include "xmalloc.h"
1b17e766
EZ
56#endif /* TEST || STATIC_MALLOC */
57
430b7832 58#if defined (HAVE_GETPWNAM) && !defined (HAVE_GETPW_DECLS)
9255ee31
EZ
59extern struct passwd *getpwuid PARAMS((uid_t));
60extern struct passwd *getpwnam PARAMS((const char *));
430b7832 61#endif /* defined (HAVE_GETPWNAM) && !HAVE_GETPW_DECLS */
d60d9f65
SS
62
63#if !defined (savestring)
9255ee31 64#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
d60d9f65
SS
65#endif /* !savestring */
66
67#if !defined (NULL)
68# if defined (__STDC__)
69# define NULL ((void *) 0)
70# else
71# define NULL 0x0
72# endif /* !__STDC__ */
73#endif /* !NULL */
74
c862e87b
JM
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. */
9255ee31
EZ
78extern char *sh_get_home_dir PARAMS((void));
79extern char *sh_get_env_value PARAMS((const char *));
c862e87b 80
d60d9f65
SS
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. */
9255ee31
EZ
84static const char *default_prefixes[] =
85 { " ~", "\t~", (const char *)NULL };
d60d9f65
SS
86
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. */
9255ee31
EZ
90static const char *default_suffixes[] =
91 { " ", "\n", (const char *)NULL };
d60d9f65
SS
92
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. */
9255ee31 97tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
d60d9f65
SS
98
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. */
9255ee31 103tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
d60d9f65
SS
104
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
107 `=~' and `:~'. */
9255ee31 108char **tilde_additional_prefixes = (char **)default_prefixes;
d60d9f65
SS
109
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
112 `:' and `=~'. */
9255ee31
EZ
113char **tilde_additional_suffixes = (char **)default_suffixes;
114
115static int tilde_find_prefix PARAMS((const char *, int *));
116static int tilde_find_suffix PARAMS((const char *));
117static char *isolate_tilde_prefix PARAMS((const char *, int *));
118static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
d60d9f65
SS
119
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. */
123static int
124tilde_find_prefix (string, len)
9255ee31 125 const char *string;
d60d9f65
SS
126 int *len;
127{
128 register int i, j, string_len;
1b17e766
EZ
129 register char **prefixes;
130
131 prefixes = tilde_additional_prefixes;
d60d9f65
SS
132
133 string_len = strlen (string);
134 *len = 0;
135
136 if (*string == '\0' || *string == '~')
137 return (0);
138
139 if (prefixes)
140 {
141 for (i = 0; i < string_len; i++)
142 {
143 for (j = 0; prefixes[j]; j++)
144 {
145 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
146 {
147 *len = strlen (prefixes[j]) - 1;
148 return (i + *len);
149 }
150 }
151 }
152 }
153 return (string_len);
154}
155
156/* Find the end of a tilde expansion in STRING, and return the index of
157 the character which ends the tilde definition. */
158static int
159tilde_find_suffix (string)
9255ee31 160 const char *string;
d60d9f65
SS
161{
162 register int i, j, string_len;
163 register char **suffixes;
164
165 suffixes = tilde_additional_suffixes;
166 string_len = strlen (string);
167
168 for (i = 0; i < string_len; i++)
169 {
1b17e766
EZ
170#if defined (__MSDOS__)
171 if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
172#else
d60d9f65 173 if (string[i] == '/' /* || !string[i] */)
1b17e766 174#endif
d60d9f65
SS
175 break;
176
177 for (j = 0; suffixes && suffixes[j]; j++)
178 {
179 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
180 return (i);
181 }
182 }
183 return (i);
184}
185
d60d9f65
SS
186/* Return a new string which is the result of tilde expanding STRING. */
187char *
188tilde_expand (string)
9255ee31 189 const char *string;
d60d9f65
SS
190{
191 char *result;
192 int result_size, result_index;
193
194 result_index = result_size = 0;
195 if (result = strchr (string, '~'))
9255ee31 196 result = (char *)xmalloc (result_size = (strlen (string) + 16));
d60d9f65 197 else
9255ee31 198 result = (char *)xmalloc (result_size = (strlen (string) + 1));
d60d9f65
SS
199
200 /* Scan through STRING expanding tildes as we come to them. */
201 while (1)
202 {
203 register int start, end;
204 char *tilde_word, *expansion;
205 int len;
206
207 /* Make START point to the tilde which starts the expansion. */
208 start = tilde_find_prefix (string, &len);
209
210 /* Copy the skipped text into the result. */
211 if ((result_index + start + 1) > result_size)
9255ee31 212 result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
d60d9f65
SS
213
214 strncpy (result + result_index, string, start);
215 result_index += start;
216
217 /* Advance STRING to the starting tilde. */
218 string += start;
219
220 /* Make END be the index of one after the last character of the
221 username. */
222 end = tilde_find_suffix (string);
223
224 /* If both START and END are zero, we are all done. */
225 if (!start && !end)
226 break;
227
228 /* Expand the entire tilde word, and copy it into RESULT. */
9255ee31 229 tilde_word = (char *)xmalloc (1 + end);
d60d9f65
SS
230 strncpy (tilde_word, string, end);
231 tilde_word[end] = '\0';
232 string += end;
233
234 expansion = tilde_expand_word (tilde_word);
235 free (tilde_word);
236
237 len = strlen (expansion);
9255ee31 238#ifdef __CYGWIN__
1b17e766 239 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
9255ee31 240 $HOME for `user' is /. On cygwin, // denotes a network drive. */
1b17e766
EZ
241 if (len > 1 || *expansion != '/' || *string != '/')
242#endif
243 {
244 if ((result_index + len + 1) > result_size)
9255ee31 245 result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
d60d9f65 246
1b17e766
EZ
247 strcpy (result + result_index, expansion);
248 result_index += len;
249 }
d60d9f65
SS
250 free (expansion);
251 }
252
253 result[result_index] = '\0';
254
255 return (result);
256}
257
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. */
261static char *
262isolate_tilde_prefix (fname, lenp)
9255ee31 263 const char *fname;
d60d9f65
SS
264 int *lenp;
265{
266 char *ret;
267 int i;
268
9255ee31 269 ret = (char *)xmalloc (strlen (fname));
1b17e766
EZ
270#if defined (__MSDOS__)
271 for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
272#else
d60d9f65 273 for (i = 1; fname[i] && fname[i] != '/'; i++)
1b17e766 274#endif
d60d9f65
SS
275 ret[i - 1] = fname[i];
276 ret[i - 1] = '\0';
277 if (lenp)
278 *lenp = i;
279 return ret;
280}
281
282/* Return a string that is PREFIX concatenated with SUFFIX starting at
283 SUFFIND. */
284static char *
285glue_prefix_and_suffix (prefix, suffix, suffind)
9255ee31
EZ
286 char *prefix;
287 const char *suffix;
d60d9f65
SS
288 int suffind;
289{
290 char *ret;
291 int plen, slen;
292
293 plen = (prefix && *prefix) ? strlen (prefix) : 0;
294 slen = strlen (suffix + suffind);
9255ee31 295 ret = (char *)xmalloc (plen + slen + 1);
1b17e766 296 if (plen)
d60d9f65
SS
297 strcpy (ret, prefix);
298 strcpy (ret + plen, suffix + suffind);
299 return ret;
300}
301
d60d9f65
SS
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. */
305char *
306tilde_expand_word (filename)
9255ee31 307 const char *filename;
d60d9f65
SS
308{
309 char *dirname, *expansion, *username;
310 int user_len;
311 struct passwd *user_entry;
312
313 if (filename == 0)
314 return ((char *)NULL);
315
316 if (*filename != '~')
317 return (savestring (filename));
318
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] == '/')
323 {
324 /* Prefix $HOME to the rest of the string. */
9255ee31 325 expansion = sh_get_env_value ("HOME");
d60d9f65
SS
326
327 /* If there is no HOME variable, look up the directory in
328 the password database. */
329 if (expansion == 0)
9255ee31 330 expansion = sh_get_home_dir ();
d60d9f65
SS
331
332 return (glue_prefix_and_suffix (expansion, filename, 1));
333 }
334
335 username = isolate_tilde_prefix (filename, &user_len);
336
337 if (tilde_expansion_preexpansion_hook)
338 {
339 expansion = (*tilde_expansion_preexpansion_hook) (username);
340 if (expansion)
341 {
342 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
343 free (username);
344 free (expansion);
345 return (dirname);
346 }
347 }
348
349 /* No preexpansion hook, or the preexpansion hook failed. Look in the
350 password database. */
351 dirname = (char *)NULL;
430b7832 352#ifdef HAVE_GETPWNAM
d60d9f65
SS
353 user_entry = getpwnam (username);
354 if (user_entry == 0)
355 {
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)
359 {
360 expansion = (*tilde_expansion_failure_hook) (username);
361 if (expansion)
362 {
363 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
364 free (expansion);
365 }
366 }
367 free (username);
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. */
370 if (dirname == 0)
371 dirname = savestring (filename);
372 }
373 else
374 {
375 free (username);
376 dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
377 }
378
379 endpwent ();
430b7832 380#endif
d60d9f65
SS
381 return (dirname);
382}
383
384\f
385#if defined (TEST)
386#undef NULL
387#include <stdio.h>
388
389main (argc, argv)
390 int argc;
391 char **argv;
392{
393 char *result, line[512];
394 int done = 0;
395
396 while (!done)
397 {
398 printf ("~expand: ");
399 fflush (stdout);
400
401 if (!gets (line))
402 strcpy (line, "done");
403
404 if ((strcmp (line, "done") == 0) ||
405 (strcmp (line, "quit") == 0) ||
406 (strcmp (line, "exit") == 0))
407 {
408 done = 1;
409 break;
410 }
411
412 result = tilde_expand (line);
413 printf (" --> %s\n", result);
414 free (result);
415 }
416 exit (0);
417}
418
419static void memory_error_and_abort ();
420
9255ee31 421static void *
d60d9f65 422xmalloc (bytes)
9255ee31 423 size_t bytes;
d60d9f65 424{
9255ee31 425 void *temp = (char *)malloc (bytes);
d60d9f65
SS
426
427 if (!temp)
428 memory_error_and_abort ();
429 return (temp);
430}
431
9255ee31 432static void *
d60d9f65 433xrealloc (pointer, bytes)
9255ee31 434 void *pointer;
d60d9f65
SS
435 int bytes;
436{
9255ee31 437 void *temp;
d60d9f65
SS
438
439 if (!pointer)
9255ee31 440 temp = malloc (bytes);
d60d9f65 441 else
9255ee31 442 temp = realloc (pointer, bytes);
d60d9f65
SS
443
444 if (!temp)
445 memory_error_and_abort ();
446
447 return (temp);
448}
449
450static void
451memory_error_and_abort ()
452{
453 fprintf (stderr, "readline: out of virtual memory\n");
454 abort ();
455}
456
457/*
458 * Local variables:
459 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
460 * end:
461 */
462#endif /* TEST */
This page took 0.348682 seconds and 4 git commands to generate.