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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 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 (TEST) || defined (STATIC_MALLOC)
51 static void *xmalloc (), *xrealloc ();
54 #endif /* TEST || STATIC_MALLOC */
56 #if !defined (HAVE_GETPW_DECLS)
57 extern struct passwd
*getpwuid
PARAMS((uid_t
));
58 extern struct passwd
*getpwnam
PARAMS((const char *));
59 #endif /* !HAVE_GETPW_DECLS */
61 #if !defined (savestring)
62 #define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
63 #endif /* !savestring */
66 # if defined (__STDC__)
67 # define NULL ((void *) 0)
70 # endif /* !__STDC__ */
73 /* If being compiled as part of bash, these will be satisfied from
74 variables.o. If being compiled as part of readline, they will
75 be satisfied from shell.o. */
76 extern char *sh_get_home_dir
PARAMS((void));
77 extern char *sh_get_env_value
PARAMS((const char *));
79 /* The default value of tilde_additional_prefixes. This is set to
80 whitespace preceding a tilde so that simple programs which do not
81 perform any word separation get desired behaviour. */
82 static const char *default_prefixes
[] =
83 { " ~", "\t~", (const char *)NULL
};
85 /* The default value of tilde_additional_suffixes. This is set to
86 whitespace or newline so that simple programs which do not
87 perform any word separation get desired behaviour. */
88 static const char *default_suffixes
[] =
89 { " ", "\n", (const char *)NULL
};
91 /* If non-null, this contains the address of a function that the application
92 wants called before trying the standard tilde expansions. The function
93 is called with the text sans tilde, and returns a malloc()'ed string
94 which is the expansion, or a NULL pointer if the expansion fails. */
95 tilde_hook_func_t
*tilde_expansion_preexpansion_hook
= (tilde_hook_func_t
*)NULL
;
97 /* If non-null, this contains the address of a function to call if the
98 standard meaning for expanding a tilde fails. The function is called
99 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
100 which is the expansion, or a NULL pointer if there is no expansion. */
101 tilde_hook_func_t
*tilde_expansion_failure_hook
= (tilde_hook_func_t
*)NULL
;
103 /* When non-null, this is a NULL terminated array of strings which
104 are duplicates for a tilde prefix. Bash uses this to expand
106 char **tilde_additional_prefixes
= (char **)default_prefixes
;
108 /* When non-null, this is a NULL terminated array of strings which match
109 the end of a username, instead of just "/". Bash sets this to
111 char **tilde_additional_suffixes
= (char **)default_suffixes
;
113 static int tilde_find_prefix
PARAMS((const char *, int *));
114 static int tilde_find_suffix
PARAMS((const char *));
115 static char *isolate_tilde_prefix
PARAMS((const char *, int *));
116 static char *glue_prefix_and_suffix
PARAMS((char *, const char *, int));
118 /* Find the start of a tilde expansion in STRING, and return the index of
119 the tilde which starts the expansion. Place the length of the text
120 which identified this tilde starter in LEN, excluding the tilde itself. */
122 tilde_find_prefix (string
, len
)
126 register int i
, j
, string_len
;
127 register char **prefixes
;
129 prefixes
= tilde_additional_prefixes
;
131 string_len
= strlen (string
);
134 if (*string
== '\0' || *string
== '~')
139 for (i
= 0; i
< string_len
; i
++)
141 for (j
= 0; prefixes
[j
]; j
++)
143 if (strncmp (string
+ i
, prefixes
[j
], strlen (prefixes
[j
])) == 0)
145 *len
= strlen (prefixes
[j
]) - 1;
154 /* Find the end of a tilde expansion in STRING, and return the index of
155 the character which ends the tilde definition. */
157 tilde_find_suffix (string
)
160 register int i
, j
, string_len
;
161 register char **suffixes
;
163 suffixes
= tilde_additional_suffixes
;
164 string_len
= strlen (string
);
166 for (i
= 0; i
< string_len
; i
++)
168 #if defined (__MSDOS__)
169 if (string
[i
] == '/' || string
[i
] == '\\' /* || !string[i] */)
171 if (string
[i
] == '/' /* || !string[i] */)
175 for (j
= 0; suffixes
&& suffixes
[j
]; j
++)
177 if (strncmp (string
+ i
, suffixes
[j
], strlen (suffixes
[j
])) == 0)
184 /* Return a new string which is the result of tilde expanding STRING. */
186 tilde_expand (string
)
190 int result_size
, result_index
;
192 result_index
= result_size
= 0;
193 if (result
= strchr (string
, '~'))
194 result
= (char *)xmalloc (result_size
= (strlen (string
) + 16));
196 result
= (char *)xmalloc (result_size
= (strlen (string
) + 1));
198 /* Scan through STRING expanding tildes as we come to them. */
201 register int start
, end
;
202 char *tilde_word
, *expansion
;
205 /* Make START point to the tilde which starts the expansion. */
206 start
= tilde_find_prefix (string
, &len
);
208 /* Copy the skipped text into the result. */
209 if ((result_index
+ start
+ 1) > result_size
)
210 result
= (char *)xrealloc (result
, 1 + (result_size
+= (start
+ 20)));
212 strncpy (result
+ result_index
, string
, start
);
213 result_index
+= start
;
215 /* Advance STRING to the starting tilde. */
218 /* Make END be the index of one after the last character of the
220 end
= tilde_find_suffix (string
);
222 /* If both START and END are zero, we are all done. */
226 /* Expand the entire tilde word, and copy it into RESULT. */
227 tilde_word
= (char *)xmalloc (1 + end
);
228 strncpy (tilde_word
, string
, end
);
229 tilde_word
[end
] = '\0';
232 expansion
= tilde_expand_word (tilde_word
);
235 len
= strlen (expansion
);
237 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
238 $HOME for `user' is /. On cygwin, // denotes a network drive. */
239 if (len
> 1 || *expansion
!= '/' || *string
!= '/')
242 if ((result_index
+ len
+ 1) > result_size
)
243 result
= (char *)xrealloc (result
, 1 + (result_size
+= (len
+ 20)));
245 strcpy (result
+ result_index
, expansion
);
251 result
[result_index
] = '\0';
256 /* Take FNAME and return the tilde prefix we want expanded. If LENP is
257 non-null, the index of the end of the prefix into FNAME is returned in
258 the location it points to. */
260 isolate_tilde_prefix (fname
, lenp
)
267 ret
= (char *)xmalloc (strlen (fname
));
268 #if defined (__MSDOS__)
269 for (i
= 1; fname
[i
] && fname
[i
] != '/' && fname
[i
] != '\\'; i
++)
271 for (i
= 1; fname
[i
] && fname
[i
] != '/'; i
++)
273 ret
[i
- 1] = fname
[i
];
280 /* Return a string that is PREFIX concatenated with SUFFIX starting at
283 glue_prefix_and_suffix (prefix
, suffix
, suffind
)
291 plen
= (prefix
&& *prefix
) ? strlen (prefix
) : 0;
292 slen
= strlen (suffix
+ suffind
);
293 ret
= (char *)xmalloc (plen
+ slen
+ 1);
295 strcpy (ret
, prefix
);
296 strcpy (ret
+ plen
, suffix
+ suffind
);
300 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
301 tilde. If there is no expansion, call tilde_expansion_failure_hook.
302 This always returns a newly-allocated string, never static storage. */
304 tilde_expand_word (filename
)
305 const char *filename
;
307 char *dirname
, *expansion
, *username
;
309 struct passwd
*user_entry
;
312 return ((char *)NULL
);
314 if (*filename
!= '~')
315 return (savestring (filename
));
317 /* A leading `~/' or a bare `~' is *always* translated to the value of
318 $HOME or the home directory of the current user, regardless of any
319 preexpansion hook. */
320 if (filename
[1] == '\0' || filename
[1] == '/')
322 /* Prefix $HOME to the rest of the string. */
323 expansion
= sh_get_env_value ("HOME");
325 /* If there is no HOME variable, look up the directory in
326 the password database. */
328 expansion
= sh_get_home_dir ();
330 return (glue_prefix_and_suffix (expansion
, filename
, 1));
333 username
= isolate_tilde_prefix (filename
, &user_len
);
335 if (tilde_expansion_preexpansion_hook
)
337 expansion
= (*tilde_expansion_preexpansion_hook
) (username
);
340 dirname
= glue_prefix_and_suffix (expansion
, filename
, user_len
);
347 /* No preexpansion hook, or the preexpansion hook failed. Look in the
348 password database. */
349 dirname
= (char *)NULL
;
350 user_entry
= getpwnam (username
);
353 /* If the calling program has a special syntax for expanding tildes,
354 and we couldn't find a standard expansion, then let them try. */
355 if (tilde_expansion_failure_hook
)
357 expansion
= (*tilde_expansion_failure_hook
) (username
);
360 dirname
= glue_prefix_and_suffix (expansion
, filename
, user_len
);
365 /* If we don't have a failure hook, or if the failure hook did not
366 expand the tilde, return a copy of what we were passed. */
368 dirname
= savestring (filename
);
373 dirname
= glue_prefix_and_suffix (user_entry
->pw_dir
, filename
, user_len
);
389 char *result
, line
[512];
394 printf ("~expand: ");
398 strcpy (line
, "done");
400 if ((strcmp (line
, "done") == 0) ||
401 (strcmp (line
, "quit") == 0) ||
402 (strcmp (line
, "exit") == 0))
408 result
= tilde_expand (line
);
409 printf (" --> %s\n", result
);
415 static void memory_error_and_abort ();
421 void *temp
= (char *)malloc (bytes
);
424 memory_error_and_abort ();
429 xrealloc (pointer
, bytes
)
436 temp
= malloc (bytes
);
438 temp
= realloc (pointer
, bytes
);
441 memory_error_and_abort ();
447 memory_error_and_abort ()
449 fprintf (stderr
, "readline: out of virtual memory\n");
455 * compile-command: "gcc -g -DTEST -o tilde tilde.c"