]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/readline/tilde.c
Imported from ../bash-2.0.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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
21
22 #if defined (HAVE_CONFIG_H)
23 # include <config.h>
24 #endif
25
26 #if defined (HAVE_STRING_H)
27 # include <string.h>
28 #else /* !HAVE_STRING_H */
29 # include <strings.h>
30 #endif /* !HAVE_STRING_H */
31
32 #if defined (HAVE_STDLIB_H)
33 # include <stdlib.h>
34 #else
35 # include "ansi_stdlib.h"
36 #endif /* HAVE_STDLIB_H */
37
38 #include <sys/types.h>
39 #include <pwd.h>
40
41 #include "tilde.h"
42
43 #if !defined (HAVE_GETPW_DECLS)
44 extern struct passwd *getpwuid (), *getpwnam ();
45 #endif /* !HAVE_GETPW_DECLS */
46
47 #if !defined (savestring)
48 extern char *xmalloc ();
49 # ifndef strcpy
50 extern char *strcpy ();
51 # endif
52 #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
53 #endif /* !savestring */
54
55 #if !defined (NULL)
56 # if defined (__STDC__)
57 # define NULL ((void *) 0)
58 # else
59 # define NULL 0x0
60 # endif /* !__STDC__ */
61 #endif /* !NULL */
62
63 #if defined (TEST) || defined (STATIC_MALLOC)
64 static char *xmalloc (), *xrealloc ();
65 #else
66 extern char *xmalloc (), *xrealloc ();
67 #endif /* TEST || STATIC_MALLOC */
68
69 /* The default value of tilde_additional_prefixes. This is set to
70 whitespace preceding a tilde so that simple programs which do not
71 perform any word separation get desired behaviour. */
72 static char *default_prefixes[] =
73 { " ~", "\t~", (char *)NULL };
74
75 /* The default value of tilde_additional_suffixes. This is set to
76 whitespace or newline so that simple programs which do not
77 perform any word separation get desired behaviour. */
78 static char *default_suffixes[] =
79 { " ", "\n", (char *)NULL };
80
81 /* If non-null, this contains the address of a function to call if the
82 standard meaning for expanding a tilde fails. The function is called
83 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
84 which is the expansion, or a NULL pointer if there is no expansion. */
85 CPFunction *tilde_expansion_failure_hook = (CPFunction *)NULL;
86
87 /* When non-null, this is a NULL terminated array of strings which
88 are duplicates for a tilde prefix. Bash uses this to expand
89 `=~' and `:~'. */
90 char **tilde_additional_prefixes = default_prefixes;
91
92 /* When non-null, this is a NULL terminated array of strings which match
93 the end of a username, instead of just "/". Bash sets this to
94 `:' and `=~'. */
95 char **tilde_additional_suffixes = default_suffixes;
96
97 /* Find the start of a tilde expansion in STRING, and return the index of
98 the tilde which starts the expansion. Place the length of the text
99 which identified this tilde starter in LEN, excluding the tilde itself. */
100 static int
101 tilde_find_prefix (string, len)
102 char *string;
103 int *len;
104 {
105 register int i, j, string_len;
106 register char **prefixes = tilde_additional_prefixes;
107
108 string_len = strlen (string);
109 *len = 0;
110
111 if (!*string || *string == '~')
112 return (0);
113
114 if (prefixes)
115 {
116 for (i = 0; i < string_len; i++)
117 {
118 for (j = 0; prefixes[j]; j++)
119 {
120 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
121 {
122 *len = strlen (prefixes[j]) - 1;
123 return (i + *len);
124 }
125 }
126 }
127 }
128 return (string_len);
129 }
130
131 /* Find the end of a tilde expansion in STRING, and return the index of
132 the character which ends the tilde definition. */
133 static int
134 tilde_find_suffix (string)
135 char *string;
136 {
137 register int i, j, string_len;
138 register char **suffixes = tilde_additional_suffixes;
139
140 string_len = strlen (string);
141
142 for (i = 0; i < string_len; i++)
143 {
144 if (string[i] == '/' || !string[i])
145 break;
146
147 for (j = 0; suffixes && suffixes[j]; j++)
148 {
149 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
150 return (i);
151 }
152 }
153 return (i);
154 }
155
156 /* Return a new string which is the result of tilde expanding STRING. */
157 char *
158 tilde_expand (string)
159 char *string;
160 {
161 char *result, *tilde_expand_word ();
162 int result_size, result_index;
163
164 result_size = result_index = 0;
165 result = (char *)NULL;
166
167 /* Scan through STRING expanding tildes as we come to them. */
168 while (1)
169 {
170 register int start, end;
171 char *tilde_word, *expansion;
172 int len;
173
174 /* Make START point to the tilde which starts the expansion. */
175 start = tilde_find_prefix (string, &len);
176
177 /* Copy the skipped text into the result. */
178 if ((result_index + start + 1) > result_size)
179 result = xrealloc (result, 1 + (result_size += (start + 20)));
180
181 strncpy (result + result_index, string, start);
182 result_index += start;
183
184 /* Advance STRING to the starting tilde. */
185 string += start;
186
187 /* Make END be the index of one after the last character of the
188 username. */
189 end = tilde_find_suffix (string);
190
191 /* If both START and END are zero, we are all done. */
192 if (!start && !end)
193 break;
194
195 /* Expand the entire tilde word, and copy it into RESULT. */
196 tilde_word = xmalloc (1 + end);
197 strncpy (tilde_word, string, end);
198 tilde_word[end] = '\0';
199 string += end;
200
201 expansion = tilde_expand_word (tilde_word);
202 free (tilde_word);
203
204 len = strlen (expansion);
205 if ((result_index + len + 1) > result_size)
206 result = xrealloc (result, 1 + (result_size += (len + 20)));
207
208 strcpy (result + result_index, expansion);
209 result_index += len;
210 free (expansion);
211 }
212
213 result[result_index] = '\0';
214
215 return (result);
216 }
217
218 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
219 tilde. If there is no expansion, call tilde_expansion_failure_hook. */
220 char *
221 tilde_expand_word (filename)
222 char *filename;
223 {
224 char *dirname;
225 char *temp_name;
226
227 if (filename == (char *)0)
228 return ((char *)NULL);
229
230 dirname = savestring (filename);
231
232 if (*dirname != '~')
233 return (dirname);
234
235 if (!dirname[1] || dirname[1] == '/')
236 {
237 /* Prepend $HOME to the rest of the string. */
238 char *temp_home = (char *)getenv ("HOME");
239 int home_len;
240
241 /* If there is no HOME variable, look up the directory in
242 the password database. */
243 if (!temp_home)
244 {
245 struct passwd *entry;
246
247 entry = getpwuid (getuid ());
248 if (entry)
249 temp_home = entry->pw_dir;
250 }
251
252 home_len = temp_home ? strlen (temp_home) : 0;
253 temp_name = xmalloc (1 + strlen (dirname + 1) + home_len);
254
255 if (temp_home)
256 strcpy (temp_name, temp_home);
257 strcpy (temp_name + home_len, dirname + 1);
258 free (dirname);
259 dirname = temp_name;
260 }
261 else
262 {
263 char *username;
264 struct passwd *user_entry;
265 int i, len;
266
267 username = xmalloc (strlen (dirname));
268 for (i = 1; dirname[i] && dirname[i] != '/'; i++)
269 username[i - 1] = dirname[i];
270 username[i - 1] = '\0';
271
272 if ((user_entry = getpwnam (username)) == (struct passwd *)0)
273 {
274 /* If the calling program has a special syntax for
275 expanding tildes, and we couldn't find a standard
276 expansion, then let them try. */
277 if (tilde_expansion_failure_hook)
278 {
279 char *expansion;
280
281 expansion = (*tilde_expansion_failure_hook) (username);
282
283 if (expansion)
284 {
285 len = strlen (expansion);
286 temp_name = xmalloc (1 + len + strlen (dirname + i));
287 strcpy (temp_name, expansion);
288 strcpy (temp_name + len, dirname + i);
289 free (expansion);
290 free (dirname);
291 dirname = temp_name;
292 }
293 }
294 /* We shouldn't report errors. */
295 }
296 else
297 {
298 len = strlen (user_entry->pw_dir);
299 temp_name = xmalloc (1 + len + strlen (dirname + i));
300 strcpy (temp_name, user_entry->pw_dir);
301 strcpy (temp_name + len, dirname + i);
302 free (dirname);
303 dirname = temp_name;
304 }
305 endpwent ();
306 free (username);
307 }
308
309 return (dirname);
310 }
311
312 \f
313 #if defined (TEST)
314 #undef NULL
315 #include <stdio.h>
316
317 main (argc, argv)
318 int argc;
319 char **argv;
320 {
321 char *result, line[512];
322 int done = 0;
323
324 while (!done)
325 {
326 printf ("~expand: ");
327 fflush (stdout);
328
329 if (!gets (line))
330 strcpy (line, "done");
331
332 if ((strcmp (line, "done") == 0) ||
333 (strcmp (line, "quit") == 0) ||
334 (strcmp (line, "exit") == 0))
335 {
336 done = 1;
337 break;
338 }
339
340 result = tilde_expand (line);
341 printf (" --> %s\n", result);
342 free (result);
343 }
344 exit (0);
345 }
346
347 static void memory_error_and_abort ();
348
349 static char *
350 xmalloc (bytes)
351 int bytes;
352 {
353 char *temp = (char *)malloc (bytes);
354
355 if (!temp)
356 memory_error_and_abort ();
357 return (temp);
358 }
359
360 static char *
361 xrealloc (pointer, bytes)
362 char *pointer;
363 int bytes;
364 {
365 char *temp;
366
367 if (!pointer)
368 temp = (char *)malloc (bytes);
369 else
370 temp = (char *)realloc (pointer, bytes);
371
372 if (!temp)
373 memory_error_and_abort ();
374
375 return (temp);
376 }
377
378 static void
379 memory_error_and_abort ()
380 {
381 fprintf (stderr, "readline: out of virtual memory\n");
382 abort ();
383 }
384
385 /*
386 * Local variables:
387 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
388 * end:
389 */
390 #endif /* TEST */