]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/readline/tilde.c
Imported from ../bash-2.05.tar.gz.
[thirdparty/bash.git] / lib / readline / tilde.c
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
10 Free Software Foundation; either version 2, or (at your option) any
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
20 Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
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 #include <pwd.h>
47
48 #include "tilde.h"
49
50 #if defined (TEST) || defined (STATIC_MALLOC)
51 static char *xmalloc (), *xrealloc ();
52 #else
53 extern char *xmalloc __P((int));
54 extern char *xrealloc __P((void *, int));
55 #endif /* TEST || STATIC_MALLOC */
56
57 #if !defined (HAVE_GETPW_DECLS)
58 extern struct passwd *getpwuid __P((uid_t));
59 extern struct passwd *getpwnam __P((const char *));
60 #endif /* !HAVE_GETPW_DECLS */
61
62 #if !defined (savestring)
63 # ifndef strcpy
64 extern 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
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. */
80 extern char *sh_get_home_dir __P((void));
81 extern char *sh_get_env_value __P((const char *));
82
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. */
86 static const char *default_prefixes[] =
87 { " ~", "\t~", (const char *)NULL };
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. */
92 static const char *default_suffixes[] =
93 { " ", "\n", (const char *)NULL };
94
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. */
99 tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
100
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. */
105 tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
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 `:~'. */
110 char **tilde_additional_prefixes = (char **)default_prefixes;
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 `=~'. */
115 char **tilde_additional_suffixes = (char **)default_suffixes;
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. */
120 static int
121 tilde_find_prefix (string, len)
122 char *string;
123 int *len;
124 {
125 register int i, j, string_len;
126 register char **prefixes;
127
128 prefixes = tilde_additional_prefixes;
129
130 string_len = strlen (string);
131 *len = 0;
132
133 if (*string == '\0' || *string == '~')
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. */
155 static int
156 tilde_find_suffix (string)
157 char *string;
158 {
159 register int i, j, string_len;
160 register char **suffixes;
161
162 suffixes = tilde_additional_suffixes;
163 string_len = strlen (string);
164
165 for (i = 0; i < string_len; i++)
166 {
167 #if defined (__MSDOS__)
168 if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
169 #else
170 if (string[i] == '/' /* || !string[i] */)
171 #endif
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. */
184 char *
185 tilde_expand (string)
186 const char *string;
187 {
188 char *result;
189 int result_size, result_index;
190
191 result_index = result_size = 0;
192 if (result = strchr (string, '~'))
193 result = xmalloc (result_size = (strlen (string) + 16));
194 else
195 result = xmalloc (result_size = (strlen (string) + 1));
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)
209 result = xrealloc (result, 1 + (result_size += (start + 20)));
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. */
226 tilde_word = xmalloc (1 + end);
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);
235 #ifdef __CYGWIN__
236 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
237 $HOME for `user' is /. On cygwin, // denotes a network drive. */
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)));
243
244 strcpy (result + result_index, expansion);
245 result_index += len;
246 }
247 free (expansion);
248 }
249
250 result[result_index] = '\0';
251
252 return (result);
253 }
254
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. */
258 static char *
259 isolate_tilde_prefix (fname, lenp)
260 char *fname;
261 int *lenp;
262 {
263 char *ret;
264 int i;
265
266 ret = xmalloc (strlen (fname));
267 #if defined (__MSDOS__)
268 for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
269 #else
270 for (i = 1; fname[i] && fname[i] != '/'; i++)
271 #endif
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. */
281 static char *
282 glue_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);
292 if (plen)
293 strcpy (ret, prefix);
294 strcpy (ret + plen, suffix + suffind);
295 return ret;
296 }
297
298 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
299 tilde. If there is no expansion, call tilde_expansion_failure_hook.
300 This always returns a newly-allocated string, never static storage. */
301 char *
302 tilde_expand_word (filename)
303 const char *filename;
304 {
305 char *dirname, *expansion, *username;
306 int user_len;
307 struct passwd *user_entry;
308
309 if (filename == 0)
310 return ((char *)NULL);
311
312 if (*filename != '~')
313 return (savestring (filename));
314
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] == '/')
319 {
320 /* Prefix $HOME to the rest of the string. */
321 expansion = sh_get_env_value ("HOME");
322
323 /* If there is no HOME variable, look up the directory in
324 the password database. */
325 if (expansion == 0)
326 expansion = sh_get_home_dir ();
327
328 return (glue_prefix_and_suffix (expansion, filename, 1));
329 }
330
331 username = isolate_tilde_prefix (filename, &user_len);
332
333 if (tilde_expansion_preexpansion_hook)
334 {
335 expansion = (*tilde_expansion_preexpansion_hook) (username);
336 if (expansion)
337 {
338 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
339 free (username);
340 free (expansion);
341 return (dirname);
342 }
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)
354 {
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 }
361 }
362 free (username);
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);
372 }
373
374 endpwent ();
375 return (dirname);
376 }
377
378 \f
379 #if defined (TEST)
380 #undef NULL
381 #include <stdio.h>
382
383 main (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
413 static void memory_error_and_abort ();
414
415 static char *
416 xmalloc (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
426 static char *
427 xrealloc (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
444 static void
445 memory_error_and_abort ()
446 {
447 fprintf (stderr, "readline: out of virtual memory\n");
448 abort ();
449 }
450
451 /*
452 * Local variables:
453 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
454 * end:
455 */
456 #endif /* TEST */