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