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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
22 #if defined (HAVE_CONFIG_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>
50 #if !defined (HAVE_GETPW_DECLS)
51 extern struct passwd
*getpwuid (), *getpwnam ();
52 #endif /* !HAVE_GETPW_DECLS */
54 #if !defined (savestring)
55 extern char *xmalloc ();
57 extern char *strcpy ();
59 #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
60 #endif /* !savestring */
63 # if defined (__STDC__)
64 # define NULL ((void *) 0)
67 # endif /* !__STDC__ */
70 #if defined (TEST) || defined (STATIC_MALLOC)
71 static char *xmalloc (), *xrealloc ();
73 extern char *xmalloc (), *xrealloc ();
74 #endif /* TEST || STATIC_MALLOC */
76 /* If being compiled as part of bash, these will be satisfied from
77 variables.o. If being compiled as part of readline, they will
78 be satisfied from shell.o. */
79 extern char *get_home_dir ();
80 extern char *get_env_value ();
82 /* The default value of tilde_additional_prefixes. This is set to
83 whitespace preceding a tilde so that simple programs which do not
84 perform any word separation get desired behaviour. */
85 static char *default_prefixes
[] =
86 { " ~", "\t~", (char *)NULL
};
88 /* The default value of tilde_additional_suffixes. This is set to
89 whitespace or newline so that simple programs which do not
90 perform any word separation get desired behaviour. */
91 static char *default_suffixes
[] =
92 { " ", "\n", (char *)NULL
};
94 /* If non-null, this contains the address of a function that the application
95 wants called before trying the standard tilde expansions. The function
96 is called with the text sans tilde, and returns a malloc()'ed string
97 which is the expansion, or a NULL pointer if the expansion fails. */
98 CPFunction
*tilde_expansion_preexpansion_hook
= (CPFunction
*)NULL
;
100 /* If non-null, this contains the address of a function to call if the
101 standard meaning for expanding a tilde fails. The function is called
102 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
103 which is the expansion, or a NULL pointer if there is no expansion. */
104 CPFunction
*tilde_expansion_failure_hook
= (CPFunction
*)NULL
;
106 /* When non-null, this is a NULL terminated array of strings which
107 are duplicates for a tilde prefix. Bash uses this to expand
109 char **tilde_additional_prefixes
= default_prefixes
;
111 /* When non-null, this is a NULL terminated array of strings which match
112 the end of a username, instead of just "/". Bash sets this to
114 char **tilde_additional_suffixes
= default_suffixes
;
116 /* Find the start of a tilde expansion in STRING, and return the index of
117 the tilde which starts the expansion. Place the length of the text
118 which identified this tilde starter in LEN, excluding the tilde itself. */
120 tilde_find_prefix (string
, len
)
124 register int i
, j
, string_len
;
125 register char **prefixes
= tilde_additional_prefixes
;
127 string_len
= strlen (string
);
130 if (*string
== '\0' || *string
== '~')
135 for (i
= 0; i
< string_len
; i
++)
137 for (j
= 0; prefixes
[j
]; j
++)
139 if (strncmp (string
+ i
, prefixes
[j
], strlen (prefixes
[j
])) == 0)
141 *len
= strlen (prefixes
[j
]) - 1;
150 /* Find the end of a tilde expansion in STRING, and return the index of
151 the character which ends the tilde definition. */
153 tilde_find_suffix (string
)
156 register int i
, j
, string_len
;
157 register char **suffixes
;
159 suffixes
= tilde_additional_suffixes
;
160 string_len
= strlen (string
);
162 for (i
= 0; i
< string_len
; i
++)
164 if (string
[i
] == '/' /* || !string[i] */)
167 for (j
= 0; suffixes
&& suffixes
[j
]; j
++)
169 if (strncmp (string
+ i
, suffixes
[j
], strlen (suffixes
[j
])) == 0)
176 /* Return a new string which is the result of tilde expanding STRING. */
178 tilde_expand (string
)
182 int result_size
, result_index
;
184 result_index
= result_size
= 0;
185 if (result
= strchr (string
, '~'))
186 result
= xmalloc (result_size
= (strlen (string
) + 16));
188 result
= xmalloc (result_size
= (strlen (string
) + 1));
190 /* Scan through STRING expanding tildes as we come to them. */
193 register int start
, end
;
194 char *tilde_word
, *expansion
;
197 /* Make START point to the tilde which starts the expansion. */
198 start
= tilde_find_prefix (string
, &len
);
200 /* Copy the skipped text into the result. */
201 if ((result_index
+ start
+ 1) > result_size
)
202 result
= xrealloc (result
, 1 + (result_size
+= (start
+ 20)));
204 strncpy (result
+ result_index
, string
, start
);
205 result_index
+= start
;
207 /* Advance STRING to the starting tilde. */
210 /* Make END be the index of one after the last character of the
212 end
= tilde_find_suffix (string
);
214 /* If both START and END are zero, we are all done. */
218 /* Expand the entire tilde word, and copy it into RESULT. */
219 tilde_word
= xmalloc (1 + end
);
220 strncpy (tilde_word
, string
, end
);
221 tilde_word
[end
] = '\0';
224 expansion
= tilde_expand_word (tilde_word
);
227 len
= strlen (expansion
);
228 if ((result_index
+ len
+ 1) > result_size
)
229 result
= xrealloc (result
, 1 + (result_size
+= (len
+ 20)));
231 strcpy (result
+ result_index
, expansion
);
236 result
[result_index
] = '\0';
241 /* Take FNAME and return the tilde prefix we want expanded. If LENP is
242 non-null, the index of the end of the prefix into FNAME is returned in
243 the location it points to. */
245 isolate_tilde_prefix (fname
, lenp
)
252 ret
= xmalloc (strlen (fname
));
253 for (i
= 1; fname
[i
] && fname
[i
] != '/'; i
++)
254 ret
[i
- 1] = fname
[i
];
261 /* Return a string that is PREFIX concatenated with SUFFIX starting at
264 glue_prefix_and_suffix (prefix
, suffix
, suffind
)
265 char *prefix
, *suffix
;
271 plen
= (prefix
&& *prefix
) ? strlen (prefix
) : 0;
272 slen
= strlen (suffix
+ suffind
);
273 ret
= xmalloc (plen
+ slen
+ 1);
274 if (prefix
&& *prefix
)
275 strcpy (ret
, prefix
);
276 strcpy (ret
+ plen
, suffix
+ suffind
);
280 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
281 tilde. If there is no expansion, call tilde_expansion_failure_hook.
282 This always returns a newly-allocated string, never static storage. */
284 tilde_expand_word (filename
)
287 char *dirname
, *expansion
, *username
;
289 struct passwd
*user_entry
;
292 return ((char *)NULL
);
294 if (*filename
!= '~')
295 return (savestring (filename
));
297 /* A leading `~/' or a bare `~' is *always* translated to the value of
298 $HOME or the home directory of the current user, regardless of any
299 preexpansion hook. */
300 if (filename
[1] == '\0' || filename
[1] == '/')
302 /* Prefix $HOME to the rest of the string. */
303 expansion
= get_env_value ("HOME");
305 /* If there is no HOME variable, look up the directory in
306 the password database. */
308 expansion
= get_home_dir ();
310 return (glue_prefix_and_suffix (expansion
, filename
, 1));
313 username
= isolate_tilde_prefix (filename
, &user_len
);
315 if (tilde_expansion_preexpansion_hook
)
317 expansion
= (*tilde_expansion_preexpansion_hook
) (username
);
320 dirname
= glue_prefix_and_suffix (expansion
, filename
, user_len
);
327 /* No preexpansion hook, or the preexpansion hook failed. Look in the
328 password database. */
329 dirname
= (char *)NULL
;
330 user_entry
= getpwnam (username
);
333 /* If the calling program has a special syntax for expanding tildes,
334 and we couldn't find a standard expansion, then let them try. */
335 if (tilde_expansion_failure_hook
)
337 expansion
= (*tilde_expansion_failure_hook
) (username
);
340 dirname
= glue_prefix_and_suffix (expansion
, filename
, user_len
);
345 /* If we don't have a failure hook, or if the failure hook did not
346 expand the tilde, return a copy of what we were passed. */
348 dirname
= savestring (filename
);
353 dirname
= glue_prefix_and_suffix (user_entry
->pw_dir
, filename
, user_len
);
369 char *result
, line
[512];
374 printf ("~expand: ");
378 strcpy (line
, "done");
380 if ((strcmp (line
, "done") == 0) ||
381 (strcmp (line
, "quit") == 0) ||
382 (strcmp (line
, "exit") == 0))
388 result
= tilde_expand (line
);
389 printf (" --> %s\n", result
);
395 static void memory_error_and_abort ();
401 char *temp
= (char *)malloc (bytes
);
404 memory_error_and_abort ();
409 xrealloc (pointer
, bytes
)
416 temp
= (char *)malloc (bytes
);
418 temp
= (char *)realloc (pointer
, bytes
);
421 memory_error_and_abort ();
427 memory_error_and_abort ()
429 fprintf (stderr
, "readline: out of virtual memory\n");
435 * compile-command: "gcc -g -DTEST -o tilde tilde.c"