]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/tilde/tilde.c
bash-5.0 distribution sources and documentation
[thirdparty/bash.git] / lib / tilde / tilde.c
1 /* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
2
3 /* Copyright (C) 1988-2017 Free Software Foundation, Inc.
4
5 This file is part of the GNU Readline Library (Readline), a library
6 for reading lines of text with interactive input and history editing.
7
8 Readline is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 Readline is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Readline. If not, see <http://www.gnu.org/licenses/>.
20 */
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 #if defined (HAVE_PWD_H)
47 #include <pwd.h>
48 #endif
49
50 #include "tilde.h"
51
52 #if defined (TEST) || defined (STATIC_MALLOC)
53 static void *xmalloc (), *xrealloc ();
54 #else
55 # include "xmalloc.h"
56 #endif /* TEST || STATIC_MALLOC */
57
58 #if !defined (HAVE_GETPW_DECLS)
59 # if defined (HAVE_GETPWUID)
60 extern struct passwd *getpwuid PARAMS((uid_t));
61 # endif
62 # if defined (HAVE_GETPWNAM)
63 extern struct passwd *getpwnam PARAMS((const char *));
64 # endif
65 #endif /* !HAVE_GETPW_DECLS */
66
67 #if !defined (savestring)
68 #define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
69 #endif /* !savestring */
70
71 #if !defined (NULL)
72 # if defined (__STDC__)
73 # define NULL ((void *) 0)
74 # else
75 # define NULL 0x0
76 # endif /* !__STDC__ */
77 #endif /* !NULL */
78
79 /* If being compiled as part of bash, these will be satisfied from
80 variables.o. If being compiled as part of readline, they will
81 be satisfied from shell.o. */
82 extern char *sh_get_home_dir PARAMS((void));
83 extern char *sh_get_env_value PARAMS((const char *));
84
85 /* The default value of tilde_additional_prefixes. This is set to
86 whitespace preceding a tilde so that simple programs which do not
87 perform any word separation get desired behaviour. */
88 static const char *default_prefixes[] =
89 { " ~", "\t~", (const char *)NULL };
90
91 /* The default value of tilde_additional_suffixes. This is set to
92 whitespace or newline so that simple programs which do not
93 perform any word separation get desired behaviour. */
94 static const char *default_suffixes[] =
95 { " ", "\n", (const char *)NULL };
96
97 /* If non-null, this contains the address of a function that the application
98 wants called before trying the standard tilde expansions. The function
99 is called with the text sans tilde, and returns a malloc()'ed string
100 which is the expansion, or a NULL pointer if the expansion fails. */
101 tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
102
103 /* If non-null, this contains the address of a function to call if the
104 standard meaning for expanding a tilde fails. The function is called
105 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
106 which is the expansion, or a NULL pointer if there is no expansion. */
107 tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
108
109 /* When non-null, this is a NULL terminated array of strings which
110 are duplicates for a tilde prefix. Bash uses this to expand
111 `=~' and `:~'. */
112 char **tilde_additional_prefixes = (char **)default_prefixes;
113
114 /* When non-null, this is a NULL terminated array of strings which match
115 the end of a username, instead of just "/". Bash sets this to
116 `:' and `=~'. */
117 char **tilde_additional_suffixes = (char **)default_suffixes;
118
119 static int tilde_find_prefix PARAMS((const char *, int *));
120 static int tilde_find_suffix PARAMS((const char *));
121 static char *isolate_tilde_prefix PARAMS((const char *, int *));
122 static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
123
124 /* Find the start of a tilde expansion in STRING, and return the index of
125 the tilde which starts the expansion. Place the length of the text
126 which identified this tilde starter in LEN, excluding the tilde itself. */
127 static int
128 tilde_find_prefix (const char *string, int *len)
129 {
130 register int i, j, string_len;
131 register char **prefixes;
132
133 prefixes = tilde_additional_prefixes;
134
135 string_len = strlen (string);
136 *len = 0;
137
138 if (*string == '\0' || *string == '~')
139 return (0);
140
141 if (prefixes)
142 {
143 for (i = 0; i < string_len; i++)
144 {
145 for (j = 0; prefixes[j]; j++)
146 {
147 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
148 {
149 *len = strlen (prefixes[j]) - 1;
150 return (i + *len);
151 }
152 }
153 }
154 }
155 return (string_len);
156 }
157
158 /* Find the end of a tilde expansion in STRING, and return the index of
159 the character which ends the tilde definition. */
160 static int
161 tilde_find_suffix (const char *string)
162 {
163 register int i, j, string_len;
164 register char **suffixes;
165
166 suffixes = tilde_additional_suffixes;
167 string_len = strlen (string);
168
169 for (i = 0; i < string_len; i++)
170 {
171 #if defined (__MSDOS__)
172 if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
173 #else
174 if (string[i] == '/' /* || !string[i] */)
175 #endif
176 break;
177
178 for (j = 0; suffixes && suffixes[j]; j++)
179 {
180 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
181 return (i);
182 }
183 }
184 return (i);
185 }
186
187 /* Return a new string which is the result of tilde expanding STRING. */
188 char *
189 tilde_expand (const char *string)
190 {
191 char *result;
192 int result_size, result_index;
193
194 result_index = result_size = 0;
195 if (result = strchr (string, '~'))
196 result = (char *)xmalloc (result_size = (strlen (string) + 16));
197 else
198 result = (char *)xmalloc (result_size = (strlen (string) + 1));
199
200 /* Scan through STRING expanding tildes as we come to them. */
201 while (1)
202 {
203 register int start, end;
204 char *tilde_word, *expansion;
205 int len;
206
207 /* Make START point to the tilde which starts the expansion. */
208 start = tilde_find_prefix (string, &len);
209
210 /* Copy the skipped text into the result. */
211 if ((result_index + start + 1) > result_size)
212 result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
213
214 strncpy (result + result_index, string, start);
215 result_index += start;
216
217 /* Advance STRING to the starting tilde. */
218 string += start;
219
220 /* Make END be the index of one after the last character of the
221 username. */
222 end = tilde_find_suffix (string);
223
224 /* If both START and END are zero, we are all done. */
225 if (!start && !end)
226 break;
227
228 /* Expand the entire tilde word, and copy it into RESULT. */
229 tilde_word = (char *)xmalloc (1 + end);
230 strncpy (tilde_word, string, end);
231 tilde_word[end] = '\0';
232 string += end;
233
234 expansion = tilde_expand_word (tilde_word);
235
236 if (expansion == 0)
237 expansion = tilde_word;
238 else
239 xfree (tilde_word);
240
241 len = strlen (expansion);
242 #ifdef __CYGWIN__
243 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
244 $HOME for `user' is /. On cygwin, // denotes a network drive. */
245 if (len > 1 || *expansion != '/' || *string != '/')
246 #endif
247 {
248 if ((result_index + len + 1) > result_size)
249 result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
250
251 strcpy (result + result_index, expansion);
252 result_index += len;
253 }
254 xfree (expansion);
255 }
256
257 result[result_index] = '\0';
258
259 return (result);
260 }
261
262 /* Take FNAME and return the tilde prefix we want expanded. If LENP is
263 non-null, the index of the end of the prefix into FNAME is returned in
264 the location it points to. */
265 static char *
266 isolate_tilde_prefix (const char *fname, int *lenp)
267 {
268 char *ret;
269 int i;
270
271 ret = (char *)xmalloc (strlen (fname));
272 #if defined (__MSDOS__)
273 for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
274 #else
275 for (i = 1; fname[i] && fname[i] != '/'; i++)
276 #endif
277 ret[i - 1] = fname[i];
278 ret[i - 1] = '\0';
279 if (lenp)
280 *lenp = i;
281 return ret;
282 }
283
284 #if 0
285 /* Public function to scan a string (FNAME) beginning with a tilde and find
286 the portion of the string that should be passed to the tilde expansion
287 function. Right now, it just calls tilde_find_suffix and allocates new
288 memory, but it can be expanded to do different things later. */
289 char *
290 tilde_find_word (const char *fname, int flags, int *lenp)
291 {
292 int x;
293 char *r;
294
295 x = tilde_find_suffix (fname);
296 if (x == 0)
297 {
298 r = savestring (fname);
299 if (lenp)
300 *lenp = 0;
301 }
302 else
303 {
304 r = (char *)xmalloc (1 + x);
305 strncpy (r, fname, x);
306 r[x] = '\0';
307 if (lenp)
308 *lenp = x;
309 }
310
311 return r;
312 }
313 #endif
314
315 /* Return a string that is PREFIX concatenated with SUFFIX starting at
316 SUFFIND. */
317 static char *
318 glue_prefix_and_suffix (char *prefix, const char *suffix, int suffind)
319 {
320 char *ret;
321 int plen, slen;
322
323 plen = (prefix && *prefix) ? strlen (prefix) : 0;
324 slen = strlen (suffix + suffind);
325 ret = (char *)xmalloc (plen + slen + 1);
326 if (plen)
327 strcpy (ret, prefix);
328 strcpy (ret + plen, suffix + suffind);
329 return ret;
330 }
331
332 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
333 tilde. If there is no expansion, call tilde_expansion_failure_hook.
334 This always returns a newly-allocated string, never static storage. */
335 char *
336 tilde_expand_word (const char *filename)
337 {
338 char *dirname, *expansion, *username;
339 int user_len;
340 struct passwd *user_entry;
341
342 if (filename == 0)
343 return ((char *)NULL);
344
345 if (*filename != '~')
346 return (savestring (filename));
347
348 /* A leading `~/' or a bare `~' is *always* translated to the value of
349 $HOME or the home directory of the current user, regardless of any
350 preexpansion hook. */
351 if (filename[1] == '\0' || filename[1] == '/')
352 {
353 /* Prefix $HOME to the rest of the string. */
354 expansion = sh_get_env_value ("HOME");
355 #if defined (_WIN32)
356 if (expansion == 0)
357 expansion = sh_get_env_value ("APPDATA");
358 #endif
359
360 /* If there is no HOME variable, look up the directory in
361 the password database. */
362 if (expansion == 0)
363 expansion = sh_get_home_dir ();
364
365 return (glue_prefix_and_suffix (expansion, filename, 1));
366 }
367
368 username = isolate_tilde_prefix (filename, &user_len);
369
370 if (tilde_expansion_preexpansion_hook)
371 {
372 expansion = (*tilde_expansion_preexpansion_hook) (username);
373 if (expansion)
374 {
375 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
376 xfree (username);
377 xfree (expansion);
378 return (dirname);
379 }
380 }
381
382 /* No preexpansion hook, or the preexpansion hook failed. Look in the
383 password database. */
384 dirname = (char *)NULL;
385 #if defined (HAVE_GETPWNAM)
386 user_entry = getpwnam (username);
387 #else
388 user_entry = 0;
389 #endif
390 if (user_entry == 0)
391 {
392 /* If the calling program has a special syntax for expanding tildes,
393 and we couldn't find a standard expansion, then let them try. */
394 if (tilde_expansion_failure_hook)
395 {
396 expansion = (*tilde_expansion_failure_hook) (username);
397 if (expansion)
398 {
399 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
400 xfree (expansion);
401 }
402 }
403 /* If we don't have a failure hook, or if the failure hook did not
404 expand the tilde, return a copy of what we were passed. */
405 if (dirname == 0)
406 dirname = savestring (filename);
407 }
408 #if defined (HAVE_GETPWENT)
409 else
410 dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
411 #endif
412
413 xfree (username);
414 #if defined (HAVE_GETPWENT)
415 endpwent ();
416 #endif
417 return (dirname);
418 }
419
420 \f
421 #if defined (TEST)
422 #undef NULL
423 #include <stdio.h>
424
425 main (int argc, char **argv)
426 {
427 char *result, line[512];
428 int done = 0;
429
430 while (!done)
431 {
432 printf ("~expand: ");
433 fflush (stdout);
434
435 if (!gets (line))
436 strcpy (line, "done");
437
438 if ((strcmp (line, "done") == 0) ||
439 (strcmp (line, "quit") == 0) ||
440 (strcmp (line, "exit") == 0))
441 {
442 done = 1;
443 break;
444 }
445
446 result = tilde_expand (line);
447 printf (" --> %s\n", result);
448 free (result);
449 }
450 exit (0);
451 }
452
453 static void memory_error_and_abort (void);
454
455 static void *
456 xmalloc (size_t bytes)
457 {
458 void *temp = (char *)malloc (bytes);
459
460 if (!temp)
461 memory_error_and_abort ();
462 return (temp);
463 }
464
465 static void *
466 xrealloc (void *pointer, int bytes)
467 {
468 void *temp;
469
470 if (!pointer)
471 temp = malloc (bytes);
472 else
473 temp = realloc (pointer, bytes);
474
475 if (!temp)
476 memory_error_and_abort ();
477
478 return (temp);
479 }
480
481 static void
482 memory_error_and_abort (void)
483 {
484 fprintf (stderr, "readline: out of virtual memory\n");
485 abort ();
486 }
487
488 /*
489 * Local variables:
490 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
491 * end:
492 */
493 #endif /* TEST */