Release 2.33.1
[external/binutils.git] / readline / 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 */