1 /* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
3 /* Copyright (C) 1988-2017 Free Software Foundation, Inc.
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.
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.
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.
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/>.
22 #if defined (HAVE_CONFIG_H)
26 #if defined (HAVE_UNISTD_H)
28 # include <sys/types.h>
33 #if defined (HAVE_STRING_H)
35 #else /* !HAVE_STRING_H */
37 #endif /* !HAVE_STRING_H */
39 #if defined (HAVE_STDLIB_H)
42 # include "ansi_stdlib.h"
43 #endif /* HAVE_STDLIB_H */
45 #include <sys/types.h>
46 #if defined (HAVE_PWD_H)
52 #if defined (TEST) || defined (STATIC_MALLOC)
53 static void *xmalloc (), *xrealloc ();
56 #endif /* TEST || STATIC_MALLOC */
58 #if !defined (HAVE_GETPW_DECLS)
59 # if defined (HAVE_GETPWUID)
60 extern struct passwd
*getpwuid
PARAMS((uid_t
));
62 # if defined (HAVE_GETPWNAM)
63 extern struct passwd
*getpwnam
PARAMS((const char *));
65 #endif /* !HAVE_GETPW_DECLS */
67 #if !defined (savestring)
68 #define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
69 #endif /* !savestring */
72 # if defined (__STDC__)
73 # define NULL ((void *) 0)
76 # endif /* !__STDC__ */
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 *));
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
};
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
};
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
;
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
;
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
112 char **tilde_additional_prefixes
= (char **)default_prefixes
;
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
117 char **tilde_additional_suffixes
= (char **)default_suffixes
;
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));
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. */
128 tilde_find_prefix (const char *string
, int *len
)
130 register int i
, j
, string_len
;
131 register char **prefixes
;
133 prefixes
= tilde_additional_prefixes
;
135 string_len
= strlen (string
);
138 if (*string
== '\0' || *string
== '~')
143 for (i
= 0; i
< string_len
; i
++)
145 for (j
= 0; prefixes
[j
]; j
++)
147 if (strncmp (string
+ i
, prefixes
[j
], strlen (prefixes
[j
])) == 0)
149 *len
= strlen (prefixes
[j
]) - 1;
158 /* Find the end of a tilde expansion in STRING, and return the index of
159 the character which ends the tilde definition. */
161 tilde_find_suffix (const char *string
)
163 register int i
, j
, string_len
;
164 register char **suffixes
;
166 suffixes
= tilde_additional_suffixes
;
167 string_len
= strlen (string
);
169 for (i
= 0; i
< string_len
; i
++)
171 #if defined (__MSDOS__)
172 if (string
[i
] == '/' || string
[i
] == '\\' /* || !string[i] */)
174 if (string
[i
] == '/' /* || !string[i] */)
178 for (j
= 0; suffixes
&& suffixes
[j
]; j
++)
180 if (strncmp (string
+ i
, suffixes
[j
], strlen (suffixes
[j
])) == 0)
187 /* Return a new string which is the result of tilde expanding STRING. */
189 tilde_expand (const char *string
)
192 int result_size
, result_index
;
194 result_index
= result_size
= 0;
195 if (result
= strchr (string
, '~'))
196 result
= (char *)xmalloc (result_size
= (strlen (string
) + 16));
198 result
= (char *)xmalloc (result_size
= (strlen (string
) + 1));
200 /* Scan through STRING expanding tildes as we come to them. */
203 register int start
, end
;
204 char *tilde_word
, *expansion
;
207 /* Make START point to the tilde which starts the expansion. */
208 start
= tilde_find_prefix (string
, &len
);
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)));
214 strncpy (result
+ result_index
, string
, start
);
215 result_index
+= start
;
217 /* Advance STRING to the starting tilde. */
220 /* Make END be the index of one after the last character of the
222 end
= tilde_find_suffix (string
);
224 /* If both START and END are zero, we are all done. */
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';
234 expansion
= tilde_expand_word (tilde_word
);
237 expansion
= tilde_word
;
241 len
= strlen (expansion
);
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
!= '/')
248 if ((result_index
+ len
+ 1) > result_size
)
249 result
= (char *)xrealloc (result
, 1 + (result_size
+= (len
+ 20)));
251 strcpy (result
+ result_index
, expansion
);
257 result
[result_index
] = '\0';
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. */
266 isolate_tilde_prefix (const char *fname
, int *lenp
)
271 ret
= (char *)xmalloc (strlen (fname
));
272 #if defined (__MSDOS__)
273 for (i
= 1; fname
[i
] && fname
[i
] != '/' && fname
[i
] != '\\'; i
++)
275 for (i
= 1; fname
[i
] && fname
[i
] != '/'; i
++)
277 ret
[i
- 1] = fname
[i
];
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. */
290 tilde_find_word (const char *fname
, int flags
, int *lenp
)
295 x
= tilde_find_suffix (fname
);
298 r
= savestring (fname
);
304 r
= (char *)xmalloc (1 + x
);
305 strncpy (r
, fname
, x
);
315 /* Return a string that is PREFIX concatenated with SUFFIX starting at
318 glue_prefix_and_suffix (char *prefix
, const char *suffix
, int suffind
)
323 plen
= (prefix
&& *prefix
) ? strlen (prefix
) : 0;
324 slen
= strlen (suffix
+ suffind
);
325 ret
= (char *)xmalloc (plen
+ slen
+ 1);
327 strcpy (ret
, prefix
);
328 strcpy (ret
+ plen
, suffix
+ suffind
);
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. */
336 tilde_expand_word (const char *filename
)
338 char *dirname
, *expansion
, *username
;
340 struct passwd
*user_entry
;
343 return ((char *)NULL
);
345 if (*filename
!= '~')
346 return (savestring (filename
));
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] == '/')
353 /* Prefix $HOME to the rest of the string. */
354 expansion
= sh_get_env_value ("HOME");
357 expansion
= sh_get_env_value ("APPDATA");
360 /* If there is no HOME variable, look up the directory in
361 the password database. */
363 expansion
= sh_get_home_dir ();
365 return (glue_prefix_and_suffix (expansion
, filename
, 1));
368 username
= isolate_tilde_prefix (filename
, &user_len
);
370 if (tilde_expansion_preexpansion_hook
)
372 expansion
= (*tilde_expansion_preexpansion_hook
) (username
);
375 dirname
= glue_prefix_and_suffix (expansion
, filename
, user_len
);
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
);
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
)
396 expansion
= (*tilde_expansion_failure_hook
) (username
);
399 dirname
= glue_prefix_and_suffix (expansion
, filename
, user_len
);
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. */
406 dirname
= savestring (filename
);
408 #if defined (HAVE_GETPWENT)
410 dirname
= glue_prefix_and_suffix (user_entry
->pw_dir
, filename
, user_len
);
414 #if defined (HAVE_GETPWENT)
425 main (int argc
, char **argv
)
427 char *result
, line
[512];
432 printf ("~expand: ");
436 strcpy (line
, "done");
438 if ((strcmp (line
, "done") == 0) ||
439 (strcmp (line
, "quit") == 0) ||
440 (strcmp (line
, "exit") == 0))
446 result
= tilde_expand (line
);
447 printf (" --> %s\n", result
);
453 static void memory_error_and_abort (void);
456 xmalloc (size_t bytes
)
458 void *temp
= (char *)malloc (bytes
);
461 memory_error_and_abort ();
466 xrealloc (void *pointer
, int bytes
)
471 temp
= malloc (bytes
);
473 temp
= realloc (pointer
, bytes
);
476 memory_error_and_abort ();
482 memory_error_and_abort (void)
484 fprintf (stderr
, "readline: out of virtual memory\n");
490 * compile-command: "gcc -g -DTEST -o tilde tilde.c"