[readline] Fix heap-buffer-overflow in update_line
[deliverable/binutils-gdb.git] / readline / tilde.c
CommitLineData
d60d9f65
SS
1/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
2
cc88a640 3/* Copyright (C) 1988-2009 Free Software Foundation, Inc.
d60d9f65 4
cc88a640
JK
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.
d60d9f65 7
cc88a640
JK
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.
d60d9f65 12
cc88a640
JK
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.
d60d9f65
SS
17
18 You should have received a copy of the GNU General Public License
cc88a640
JK
19 along with Readline. If not, see <http://www.gnu.org/licenses/>.
20*/
d60d9f65
SS
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>
5bdf8622 46#if defined (HAVE_PWD_H)
d60d9f65 47#include <pwd.h>
5bdf8622 48#endif
d60d9f65
SS
49
50#include "tilde.h"
51
1b17e766 52#if defined (TEST) || defined (STATIC_MALLOC)
9255ee31 53static void *xmalloc (), *xrealloc ();
1b17e766 54#else
9255ee31 55# include "xmalloc.h"
1b17e766
EZ
56#endif /* TEST || STATIC_MALLOC */
57
5bdf8622
DJ
58#if !defined (HAVE_GETPW_DECLS)
59# if defined (HAVE_GETPWUID)
9255ee31 60extern struct passwd *getpwuid PARAMS((uid_t));
5bdf8622
DJ
61# endif
62# if defined (HAVE_GETPWNAM)
9255ee31 63extern struct passwd *getpwnam PARAMS((const char *));
5bdf8622
DJ
64# endif
65#endif /* !HAVE_GETPW_DECLS */
d60d9f65
SS
66
67#if !defined (savestring)
9255ee31 68#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
d60d9f65
SS
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
c862e87b
JM
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. */
9255ee31
EZ
82extern char *sh_get_home_dir PARAMS((void));
83extern char *sh_get_env_value PARAMS((const char *));
c862e87b 84
d60d9f65
SS
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. */
9255ee31
EZ
88static const char *default_prefixes[] =
89 { " ~", "\t~", (const char *)NULL };
d60d9f65
SS
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. */
9255ee31
EZ
94static const char *default_suffixes[] =
95 { " ", "\n", (const char *)NULL };
d60d9f65
SS
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. */
9255ee31 101tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
d60d9f65
SS
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. */
9255ee31 107tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
d60d9f65
SS
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 `:~'. */
9255ee31 112char **tilde_additional_prefixes = (char **)default_prefixes;
d60d9f65
SS
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 `=~'. */
9255ee31
EZ
117char **tilde_additional_suffixes = (char **)default_suffixes;
118
119static int tilde_find_prefix PARAMS((const char *, int *));
120static int tilde_find_suffix PARAMS((const char *));
121static char *isolate_tilde_prefix PARAMS((const char *, int *));
122static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
d60d9f65
SS
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. */
127static int
128tilde_find_prefix (string, len)
9255ee31 129 const char *string;
d60d9f65
SS
130 int *len;
131{
132 register int i, j, string_len;
1b17e766
EZ
133 register char **prefixes;
134
135 prefixes = tilde_additional_prefixes;
d60d9f65
SS
136
137 string_len = strlen (string);
138 *len = 0;
139
140 if (*string == '\0' || *string == '~')
141 return (0);
142
143 if (prefixes)
144 {
145 for (i = 0; i < string_len; i++)
146 {
147 for (j = 0; prefixes[j]; j++)
148 {
149 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
150 {
151 *len = strlen (prefixes[j]) - 1;
152 return (i + *len);
153 }
154 }
155 }
156 }
157 return (string_len);
158}
159
160/* Find the end of a tilde expansion in STRING, and return the index of
161 the character which ends the tilde definition. */
162static int
163tilde_find_suffix (string)
9255ee31 164 const char *string;
d60d9f65
SS
165{
166 register int i, j, string_len;
167 register char **suffixes;
168
169 suffixes = tilde_additional_suffixes;
170 string_len = strlen (string);
171
172 for (i = 0; i < string_len; i++)
173 {
1b17e766
EZ
174#if defined (__MSDOS__)
175 if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
176#else
d60d9f65 177 if (string[i] == '/' /* || !string[i] */)
1b17e766 178#endif
d60d9f65
SS
179 break;
180
181 for (j = 0; suffixes && suffixes[j]; j++)
182 {
183 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
184 return (i);
185 }
186 }
187 return (i);
188}
189
d60d9f65
SS
190/* Return a new string which is the result of tilde expanding STRING. */
191char *
192tilde_expand (string)
9255ee31 193 const char *string;
d60d9f65
SS
194{
195 char *result;
196 int result_size, result_index;
197
198 result_index = result_size = 0;
199 if (result = strchr (string, '~'))
9255ee31 200 result = (char *)xmalloc (result_size = (strlen (string) + 16));
d60d9f65 201 else
9255ee31 202 result = (char *)xmalloc (result_size = (strlen (string) + 1));
d60d9f65
SS
203
204 /* Scan through STRING expanding tildes as we come to them. */
205 while (1)
206 {
207 register int start, end;
208 char *tilde_word, *expansion;
209 int len;
210
211 /* Make START point to the tilde which starts the expansion. */
212 start = tilde_find_prefix (string, &len);
213
214 /* Copy the skipped text into the result. */
215 if ((result_index + start + 1) > result_size)
9255ee31 216 result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
d60d9f65
SS
217
218 strncpy (result + result_index, string, start);
219 result_index += start;
220
221 /* Advance STRING to the starting tilde. */
222 string += start;
223
224 /* Make END be the index of one after the last character of the
225 username. */
226 end = tilde_find_suffix (string);
227
228 /* If both START and END are zero, we are all done. */
229 if (!start && !end)
230 break;
231
232 /* Expand the entire tilde word, and copy it into RESULT. */
9255ee31 233 tilde_word = (char *)xmalloc (1 + end);
d60d9f65
SS
234 strncpy (tilde_word, string, end);
235 tilde_word[end] = '\0';
236 string += end;
237
238 expansion = tilde_expand_word (tilde_word);
cc88a640 239 xfree (tilde_word);
d60d9f65
SS
240
241 len = strlen (expansion);
9255ee31 242#ifdef __CYGWIN__
1b17e766 243 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
9255ee31 244 $HOME for `user' is /. On cygwin, // denotes a network drive. */
1b17e766
EZ
245 if (len > 1 || *expansion != '/' || *string != '/')
246#endif
247 {
248 if ((result_index + len + 1) > result_size)
9255ee31 249 result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
d60d9f65 250
1b17e766
EZ
251 strcpy (result + result_index, expansion);
252 result_index += len;
253 }
cc88a640 254 xfree (expansion);
d60d9f65
SS
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. */
265static char *
266isolate_tilde_prefix (fname, lenp)
9255ee31 267 const char *fname;
d60d9f65
SS
268 int *lenp;
269{
270 char *ret;
271 int i;
272
9255ee31 273 ret = (char *)xmalloc (strlen (fname));
1b17e766
EZ
274#if defined (__MSDOS__)
275 for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
276#else
d60d9f65 277 for (i = 1; fname[i] && fname[i] != '/'; i++)
1b17e766 278#endif
d60d9f65
SS
279 ret[i - 1] = fname[i];
280 ret[i - 1] = '\0';
281 if (lenp)
282 *lenp = i;
283 return ret;
284}
285
5bdf8622
DJ
286#if 0
287/* Public function to scan a string (FNAME) beginning with a tilde and find
288 the portion of the string that should be passed to the tilde expansion
289 function. Right now, it just calls tilde_find_suffix and allocates new
290 memory, but it can be expanded to do different things later. */
291char *
292tilde_find_word (fname, flags, lenp)
293 const char *fname;
294 int flags, *lenp;
295{
296 int x;
297 char *r;
298
299 x = tilde_find_suffix (fname);
300 if (x == 0)
301 {
302 r = savestring (fname);
303 if (lenp)
304 *lenp = 0;
305 }
306 else
307 {
308 r = (char *)xmalloc (1 + x);
309 strncpy (r, fname, x);
310 r[x] = '\0';
311 if (lenp)
312 *lenp = x;
313 }
314
315 return r;
316}
317#endif
318
d60d9f65
SS
319/* Return a string that is PREFIX concatenated with SUFFIX starting at
320 SUFFIND. */
321static char *
322glue_prefix_and_suffix (prefix, suffix, suffind)
9255ee31
EZ
323 char *prefix;
324 const char *suffix;
d60d9f65
SS
325 int suffind;
326{
327 char *ret;
328 int plen, slen;
329
330 plen = (prefix && *prefix) ? strlen (prefix) : 0;
331 slen = strlen (suffix + suffind);
9255ee31 332 ret = (char *)xmalloc (plen + slen + 1);
1b17e766 333 if (plen)
d60d9f65
SS
334 strcpy (ret, prefix);
335 strcpy (ret + plen, suffix + suffind);
336 return ret;
337}
338
d60d9f65
SS
339/* Do the work of tilde expansion on FILENAME. FILENAME starts with a
340 tilde. If there is no expansion, call tilde_expansion_failure_hook.
341 This always returns a newly-allocated string, never static storage. */
342char *
343tilde_expand_word (filename)
9255ee31 344 const char *filename;
d60d9f65
SS
345{
346 char *dirname, *expansion, *username;
347 int user_len;
348 struct passwd *user_entry;
349
350 if (filename == 0)
351 return ((char *)NULL);
352
353 if (*filename != '~')
354 return (savestring (filename));
355
356 /* A leading `~/' or a bare `~' is *always* translated to the value of
357 $HOME or the home directory of the current user, regardless of any
358 preexpansion hook. */
359 if (filename[1] == '\0' || filename[1] == '/')
360 {
361 /* Prefix $HOME to the rest of the string. */
9255ee31 362 expansion = sh_get_env_value ("HOME");
7f3c5ec8
EZ
363#ifdef _WIN32
364 if (!expansion)
365 expansion = sh_get_env_value ("APPDATA");
366#endif
d60d9f65
SS
367
368 /* If there is no HOME variable, look up the directory in
369 the password database. */
370 if (expansion == 0)
9255ee31 371 expansion = sh_get_home_dir ();
d60d9f65
SS
372
373 return (glue_prefix_and_suffix (expansion, filename, 1));
374 }
375
376 username = isolate_tilde_prefix (filename, &user_len);
377
378 if (tilde_expansion_preexpansion_hook)
379 {
380 expansion = (*tilde_expansion_preexpansion_hook) (username);
381 if (expansion)
382 {
383 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
cc88a640
JK
384 xfree (username);
385 xfree (expansion);
d60d9f65
SS
386 return (dirname);
387 }
388 }
389
390 /* No preexpansion hook, or the preexpansion hook failed. Look in the
391 password database. */
392 dirname = (char *)NULL;
5bdf8622 393#if defined (HAVE_GETPWNAM)
d60d9f65 394 user_entry = getpwnam (username);
5bdf8622
DJ
395#else
396 user_entry = 0;
397#endif
d60d9f65
SS
398 if (user_entry == 0)
399 {
400 /* If the calling program has a special syntax for expanding tildes,
401 and we couldn't find a standard expansion, then let them try. */
402 if (tilde_expansion_failure_hook)
403 {
404 expansion = (*tilde_expansion_failure_hook) (username);
405 if (expansion)
406 {
407 dirname = glue_prefix_and_suffix (expansion, filename, user_len);
cc88a640 408 xfree (expansion);
d60d9f65
SS
409 }
410 }
d60d9f65
SS
411 /* If we don't have a failure hook, or if the failure hook did not
412 expand the tilde, return a copy of what we were passed. */
413 if (dirname == 0)
414 dirname = savestring (filename);
415 }
5bdf8622 416#if defined (HAVE_GETPWENT)
d60d9f65 417 else
cc88a640
JK
418 dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
419#endif
420
421 xfree (username);
422#if defined (HAVE_GETPWENT)
d60d9f65 423 endpwent ();
430b7832 424#endif
d60d9f65
SS
425 return (dirname);
426}
427
428\f
429#if defined (TEST)
430#undef NULL
431#include <stdio.h>
432
433main (argc, argv)
434 int argc;
435 char **argv;
436{
437 char *result, line[512];
438 int done = 0;
439
440 while (!done)
441 {
442 printf ("~expand: ");
443 fflush (stdout);
444
445 if (!gets (line))
446 strcpy (line, "done");
447
448 if ((strcmp (line, "done") == 0) ||
449 (strcmp (line, "quit") == 0) ||
450 (strcmp (line, "exit") == 0))
451 {
452 done = 1;
453 break;
454 }
455
456 result = tilde_expand (line);
457 printf (" --> %s\n", result);
458 free (result);
459 }
460 exit (0);
461}
462
463static void memory_error_and_abort ();
464
9255ee31 465static void *
d60d9f65 466xmalloc (bytes)
9255ee31 467 size_t bytes;
d60d9f65 468{
9255ee31 469 void *temp = (char *)malloc (bytes);
d60d9f65
SS
470
471 if (!temp)
472 memory_error_and_abort ();
473 return (temp);
474}
475
9255ee31 476static void *
d60d9f65 477xrealloc (pointer, bytes)
9255ee31 478 void *pointer;
d60d9f65
SS
479 int bytes;
480{
9255ee31 481 void *temp;
d60d9f65
SS
482
483 if (!pointer)
9255ee31 484 temp = malloc (bytes);
d60d9f65 485 else
9255ee31 486 temp = realloc (pointer, bytes);
d60d9f65
SS
487
488 if (!temp)
489 memory_error_and_abort ();
490
491 return (temp);
492}
493
494static void
495memory_error_and_abort ()
496{
497 fprintf (stderr, "readline: out of virtual memory\n");
498 abort ();
499}
500
501/*
502 * Local variables:
503 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
504 * end:
505 */
506#endif /* TEST */
This page took 1.06292 seconds and 4 git commands to generate.