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