]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/tilde/tilde.c
Imported from ../bash-2.01.1.tar.gz.
[thirdparty/bash.git] / lib / tilde / 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_UNISTD_H)
27 # include <unistd.h>
28 #endif
29
30 #if defined (HAVE_STRING_H)
31 # include <string.h>
32 #else /* !HAVE_STRING_H */
33 # include <strings.h>
34 #endif /* !HAVE_STRING_H */
35
36 #if defined (HAVE_STDLIB_H)
37 # include <stdlib.h>
38 #else
39 # include "ansi_stdlib.h"
40 #endif /* HAVE_STDLIB_H */
41
42 #include <sys/types.h>
43 #include <pwd.h>
44
45 #include "tilde.h"
46
47 #ifdef SHELL
48 #include "shell.h"
49 #endif
50
51 #if !defined (HAVE_GETPW_DECLS)
52 extern struct passwd *getpwuid (), *getpwnam ();
53 #endif /* !HAVE_GETPW_DECLS */
54
55 #if !defined (savestring)
56 extern char *xmalloc ();
57 # ifndef strcpy
58 extern char *strcpy ();
59 # endif
60 #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
61 #endif /* !savestring */
62
63 #if !defined (NULL)
64 # if defined (__STDC__)
65 # define NULL ((void *) 0)
66 # else
67 # define NULL 0x0
68 # endif /* !__STDC__ */
69 #endif /* !NULL */
70
71 #if defined (TEST) || defined (STATIC_MALLOC)
72 static char *xmalloc (), *xrealloc ();
73 #else
74 extern char *xmalloc (), *xrealloc ();
75 #endif /* TEST || STATIC_MALLOC */
76
77 /* The default value of tilde_additional_prefixes. This is set to
78 whitespace preceding a tilde so that simple programs which do not
79 perform any word separation get desired behaviour. */
80 static char *default_prefixes[] =
81 { " ~", "\t~", (char *)NULL };
82
83 /* The default value of tilde_additional_suffixes. This is set to
84 whitespace or newline so that simple programs which do not
85 perform any word separation get desired behaviour. */
86 static char *default_suffixes[] =
87 { " ", "\n", (char *)NULL };
88
89 /* If non-null, this contains the address of a function that the application
90 wants called before trying the standard tilde expansions. The function
91 is called with the text sans tilde, and returns a malloc()'ed string
92 which is the expansion, or a NULL pointer if the expansion fails. */
93 CPFunction *tilde_expansion_preexpansion_hook = (CPFunction *)NULL;
94
95 /* If non-null, this contains the address of a function to call if the
96 standard meaning for expanding a tilde fails. The function is called
97 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
98 which is the expansion, or a NULL pointer if there is no expansion. */
99 CPFunction *tilde_expansion_failure_hook = (CPFunction *)NULL;
100
101 /* When non-null, this is a NULL terminated array of strings which
102 are duplicates for a tilde prefix. Bash uses this to expand
103 `=~' and `:~'. */
104 char **tilde_additional_prefixes = default_prefixes;
105
106 /* When non-null, this is a NULL terminated array of strings which match
107 the end of a username, instead of just "/". Bash sets this to
108 `:' and `=~'. */
109 char **tilde_additional_suffixes = default_suffixes;
110
111 /* Find the start of a tilde expansion in STRING, and return the index of
112 the tilde which starts the expansion. Place the length of the text
113 which identified this tilde starter in LEN, excluding the tilde itself. */
114 static int
115 tilde_find_prefix (string, len)
116 char *string;
117 int *len;
118 {
119 register int i, j, string_len;
120 register char **prefixes = tilde_additional_prefixes;
121
122 string_len = strlen (string);
123 *len = 0;
124
125 if (*string == '\0' || *string == '~')
126 return (0);
127
128 if (prefixes)
129 {
130 for (i = 0; i < string_len; i++)
131 {
132 for (j = 0; prefixes[j]; j++)
133 {
134 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
135 {
136 *len = strlen (prefixes[j]) - 1;
137 return (i + *len);
138 }
139 }
140 }
141 }
142 return (string_len);
143 }
144
145 /* Find the end of a tilde expansion in STRING, and return the index of
146 the character which ends the tilde definition. */
147 static int
148 tilde_find_suffix (string)
149 char *string;
150 {
151 register int i, j, string_len;
152 register char **suffixes;
153
154 suffixes = tilde_additional_suffixes;
155 string_len = strlen (string);
156
157 for (i = 0; i < string_len; i++)
158 {
159 if (string[i] == '/' /* || !string[i] */)
160 break;
161
162 for (j = 0; suffixes && suffixes[j]; j++)
163 {
164 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
165 return (i);
166 }
167 }
168 return (i);
169 }
170
171 #if !defined (SHELL)
172 static char *
173 get_string_value (varname)
174 char *varname;
175 {
176 return ((char *)getenv (varname));
177 }
178 #endif
179
180 /* Return a new string which is the result of tilde expanding STRING. */
181 char *
182 tilde_expand (string)
183 char *string;
184 {
185 char *result;
186 int result_size, result_index;
187
188 result_index = result_size = 0;
189 if (result = strchr (string, '~'))
190 result = xmalloc (result_size = (strlen (string) + 16));
191 else
192 result = xmalloc (result_size = (strlen (string) + 1));
193
194 /* Scan through STRING expanding tildes as we come to them. */
195 while (1)
196 {
197 register int start, end;
198 char *tilde_word, *expansion;
199 int len;
200
201 /* Make START point to the tilde which starts the expansion. */
202 start = tilde_find_prefix (string, &len);
203
204 /* Copy the skipped text into the result. */
205 if ((result_index + start + 1) > result_size)
206 result = xrealloc (result, 1 + (result_size += (start + 20)));
207
208 strncpy (result + result_index, string, start);
209 result_index += start;
210
211 /* Advance STRING to the starting tilde. */
212 string += start;
213
214 /* Make END be the index of one after the last character of the
215 username. */
216 end = tilde_find_suffix (string);
217
218 /* If both START and END are zero, we are all done. */
219 if (!start && !end)
220 break;
221
222 /* Expand the entire tilde word, and copy it into RESULT. */
223 tilde_word = xmalloc (1 + end);
224 strncpy (tilde_word, string, end);
225 tilde_word[end] = '\0';
226 string += end;
227
228 expansion = tilde_expand_word (tilde_word);
229 free (tilde_word);
230
231 len = strlen (expansion);
232 if ((result_index + len + 1) > result_size)
233 result = xrealloc (result, 1 + (result_size += (len + 20)));
234
235 strcpy (result + result_index, expansion);
236 result_index += len;
237 free (expansion);
238 }
239
240 result[result_index] = '\0';
241
242 return (result);
243 }
244
245 /* Take FNAME and return the tilde prefix we want expanded. If LENP is
246 non-null, the index of the end of the prefix into FNAME is returned in
247 the location it points to. */
248 static char *
249 isolate_tilde_prefix (fname, lenp)
250 char *fname;
251 int *lenp;
252 {
253 char *ret;
254 int i;
255
256 ret = xmalloc (strlen (fname));
257 for (i = 1; fname[i] && fname[i] != '/'; i++)
258 ret[i - 1] = fname[i];
259 ret[i - 1] = '\0';
260 if (lenp)
261 *lenp = i;
262 return ret;
263 }
264
265 /* Return a string that is PREFIX concatenated with SUFFIX starting at
266 SUFFIND. */
267 static char *
268 glue_prefix_and_suffix (prefix, suffix, suffind)
269 char *prefix, *suffix;
270 int suffind;
271 {
272 char *ret;
273 int plen, slen;
274
275 plen = (prefix && *prefix) ? strlen (prefix) : 0;
276 slen = strlen (suffix + suffind);
277 ret = xmalloc (plen + slen + 1);
278 if (prefix && *prefix)
279 strcpy (ret, prefix);
280 strcpy (ret + plen, suffix + suffind);
281 return ret;
282 }
283
284 static char *
285 get_home_dir ()
286 {
287 char *home_dir;
288
289 #ifdef SHELL
290 home_dir = (char *)NULL;
291 if (current_user.home_dir == 0)
292 get_current_user_info ();
293 home_dir = current_user.home_dir;
294 #else
295 struct passwd *entry;
296
297 home_dir = (char *)NULL;
298 entry = getpwuid (getuid ());
299 if (entry)
300 home_dir = entry->pw_dir;
301 #endif
302 return (home_dir);
303 }
304
305 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
306 tilde. If there is no expansion, call tilde_expansion_failure_hook.
307 This always returns a newly-allocated string, never static storage. */
308 char *
309 tilde_expand_word (filename)
310 char *filename;
311 {
312 char *dirname, *expansion, *username;
313 int user_len;
314 struct passwd *user_entry;
315
316 if (filename == 0)
317 return ((char *)NULL);
318
319 if (*filename != '~')
320 return (savestring (filename));
321
322 /* A leading `~/' or a bare `~' is *always* translated to the value of
323 $HOME or the home directory of the current user, regardless of any
324 preexpansion hook. */
325 if (filename[1] == '\0' || filename[1] == '/')
326 {
327 /* Prefix $HOME to the rest of the string. */
328 expansion = get_string_value ("HOME");
329
330 /* If there is no HOME variable, look up the directory in
331 the password database. */
332 if (expansion == 0)
333 expansion = get_home_dir ();
334
335 return (glue_prefix_and_suffix (expansion, filename, 1));
336 }
337
338 username = isolate_tilde_prefix (filename, &user_len);
339
340 if (tilde_expansion_preexpansion_hook)
341 {
342 expansion = (*tilde_expansion_preexpansion_hook) (username);
343 if (expansion)
344 {
345 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
346 free (username);
347 free (expansion);
348 return (dirname);
349 }
350 }
351
352 /* No preexpansion hook, or the preexpansion hook failed. Look in the
353 password database. */
354 dirname = (char *)NULL;
355 user_entry = getpwnam (username);
356 if (user_entry == 0)
357 {
358 /* If the calling program has a special syntax for expanding tildes,
359 and we couldn't find a standard expansion, then let them try. */
360 if (tilde_expansion_failure_hook)
361 {
362 expansion = (*tilde_expansion_failure_hook) (username);
363 if (expansion)
364 {
365 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
366 free (expansion);
367 }
368 }
369 free (username);
370 /* If we don't have a failure hook, or if the failure hook did not
371 expand the tilde, return a copy of what we were passed. */
372 if (dirname == 0)
373 dirname = savestring (filename);
374 }
375 else
376 {
377 free (username);
378 dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
379 }
380
381 endpwent ();
382 return (dirname);
383 }
384
385 \f
386 #if defined (TEST)
387 #undef NULL
388 #include <stdio.h>
389
390 main (argc, argv)
391 int argc;
392 char **argv;
393 {
394 char *result, line[512];
395 int done = 0;
396
397 while (!done)
398 {
399 printf ("~expand: ");
400 fflush (stdout);
401
402 if (!gets (line))
403 strcpy (line, "done");
404
405 if ((strcmp (line, "done") == 0) ||
406 (strcmp (line, "quit") == 0) ||
407 (strcmp (line, "exit") == 0))
408 {
409 done = 1;
410 break;
411 }
412
413 result = tilde_expand (line);
414 printf (" --> %s\n", result);
415 free (result);
416 }
417 exit (0);
418 }
419
420 static void memory_error_and_abort ();
421
422 static char *
423 xmalloc (bytes)
424 int bytes;
425 {
426 char *temp = (char *)malloc (bytes);
427
428 if (!temp)
429 memory_error_and_abort ();
430 return (temp);
431 }
432
433 static char *
434 xrealloc (pointer, bytes)
435 char *pointer;
436 int bytes;
437 {
438 char *temp;
439
440 if (!pointer)
441 temp = (char *)malloc (bytes);
442 else
443 temp = (char *)realloc (pointer, bytes);
444
445 if (!temp)
446 memory_error_and_abort ();
447
448 return (temp);
449 }
450
451 static void
452 memory_error_and_abort ()
453 {
454 fprintf (stderr, "readline: out of virtual memory\n");
455 abort ();
456 }
457
458 /*
459 * Local variables:
460 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
461 * end:
462 */
463 #endif /* TEST */