source: trunk/third/readline/tilde.c @ 17010

Revision 17010, 12.1 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17009, which included commits to RCS files with non-trunk default branches.
Line 
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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 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 (TEST) || defined (STATIC_MALLOC)
51static void *xmalloc (), *xrealloc ();
52#else
53#  include "xmalloc.h"
54#endif /* TEST || STATIC_MALLOC */
55
56#if !defined (HAVE_GETPW_DECLS)
57extern struct passwd *getpwuid PARAMS((uid_t));
58extern struct passwd *getpwnam PARAMS((const char *));
59#endif /* !HAVE_GETPW_DECLS */
60
61#if !defined (savestring)
62#  ifndef strcpy
63extern char *strcpy ();
64#  endif
65#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
66#endif /* !savestring */
67
68#if !defined (NULL)
69#  if defined (__STDC__)
70#    define NULL ((void *) 0)
71#  else
72#    define NULL 0x0
73#  endif /* !__STDC__ */
74#endif /* !NULL */
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. */
79extern char *sh_get_home_dir PARAMS((void));
80extern char *sh_get_env_value PARAMS((const char *));
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. */
85static const char *default_prefixes[] =
86  { " ~", "\t~", (const 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. */
91static const char *default_suffixes[] =
92  { " ", "\n", (const 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. */
98tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)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. */
104tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)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 `:~'. */
109char **tilde_additional_prefixes = (char **)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 `=~'. */
114char **tilde_additional_suffixes = (char **)default_suffixes;
115
116static int tilde_find_prefix PARAMS((const char *, int *));
117static int tilde_find_suffix PARAMS((const char *));
118static char *isolate_tilde_prefix PARAMS((const char *, int *));
119static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
120
121/* Find the start of a tilde expansion in STRING, and return the index of
122   the tilde which starts the expansion.  Place the length of the text
123   which identified this tilde starter in LEN, excluding the tilde itself. */
124static int
125tilde_find_prefix (string, len)
126     const char *string;
127     int *len;
128{
129  register int i, j, string_len;
130  register char **prefixes;
131
132  prefixes = tilde_additional_prefixes;
133
134  string_len = strlen (string);
135  *len = 0;
136
137  if (*string == '\0' || *string == '~')
138    return (0);
139
140  if (prefixes)
141    {
142      for (i = 0; i < string_len; i++)
143        {
144          for (j = 0; prefixes[j]; j++)
145            {
146              if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
147                {
148                  *len = strlen (prefixes[j]) - 1;
149                  return (i + *len);
150                }
151            }
152        }
153    }
154  return (string_len);
155}
156
157/* Find the end of a tilde expansion in STRING, and return the index of
158   the character which ends the tilde definition.  */
159static int
160tilde_find_suffix (string)
161     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. */
188char *
189tilde_expand (string)
190     const char *string;
191{
192  char *result;
193  int result_size, result_index;
194
195  result_index = result_size = 0;
196  if (result = strchr (string, '~'))
197    result = (char *)xmalloc (result_size = (strlen (string) + 16));
198  else
199    result = (char *)xmalloc (result_size = (strlen (string) + 1));
200
201  /* Scan through STRING expanding tildes as we come to them. */
202  while (1)
203    {
204      register int start, end;
205      char *tilde_word, *expansion;
206      int len;
207
208      /* Make START point to the tilde which starts the expansion. */
209      start = tilde_find_prefix (string, &len);
210
211      /* Copy the skipped text into the result. */
212      if ((result_index + start + 1) > result_size)
213        result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
214
215      strncpy (result + result_index, string, start);
216      result_index += start;
217
218      /* Advance STRING to the starting tilde. */
219      string += start;
220
221      /* Make END be the index of one after the last character of the
222         username. */
223      end = tilde_find_suffix (string);
224
225      /* If both START and END are zero, we are all done. */
226      if (!start && !end)
227        break;
228
229      /* Expand the entire tilde word, and copy it into RESULT. */
230      tilde_word = (char *)xmalloc (1 + end);
231      strncpy (tilde_word, string, end);
232      tilde_word[end] = '\0';
233      string += end;
234
235      expansion = tilde_expand_word (tilde_word);
236      free (tilde_word);
237
238      len = strlen (expansion);
239#ifdef __CYGWIN__
240      /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
241         $HOME for `user' is /.  On cygwin, // denotes a network drive. */
242      if (len > 1 || *expansion != '/' || *string != '/')
243#endif
244        {
245          if ((result_index + len + 1) > result_size)
246            result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
247
248          strcpy (result + result_index, expansion);
249          result_index += len;
250        }
251      free (expansion);
252    }
253
254  result[result_index] = '\0';
255
256  return (result);
257}
258
259/* Take FNAME and return the tilde prefix we want expanded.  If LENP is
260   non-null, the index of the end of the prefix into FNAME is returned in
261   the location it points to. */
262static char *
263isolate_tilde_prefix (fname, lenp)
264     const char *fname;
265     int *lenp;
266{
267  char *ret;
268  int i;
269
270  ret = (char *)xmalloc (strlen (fname));
271#if defined (__MSDOS__)
272  for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
273#else
274  for (i = 1; fname[i] && fname[i] != '/'; i++)
275#endif
276    ret[i - 1] = fname[i];
277  ret[i - 1] = '\0';
278  if (lenp)
279    *lenp = i;
280  return ret;
281}
282
283/* Return a string that is PREFIX concatenated with SUFFIX starting at
284   SUFFIND. */
285static char *
286glue_prefix_and_suffix (prefix, suffix, suffind)
287     char *prefix;
288     const char *suffix;
289     int suffind;
290{
291  char *ret;
292  int plen, slen;
293
294  plen = (prefix && *prefix) ? strlen (prefix) : 0;
295  slen = strlen (suffix + suffind);
296  ret = (char *)xmalloc (plen + slen + 1);
297  if (plen)
298    strcpy (ret, prefix);
299  strcpy (ret + plen, suffix + suffind);
300  return ret;
301}
302
303/* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
304   tilde.  If there is no expansion, call tilde_expansion_failure_hook.
305   This always returns a newly-allocated string, never static storage. */
306char *
307tilde_expand_word (filename)
308     const char *filename;
309{
310  char *dirname, *expansion, *username;
311  int user_len;
312  struct passwd *user_entry;
313
314  if (filename == 0)
315    return ((char *)NULL);
316
317  if (*filename != '~')
318    return (savestring (filename));
319
320  /* A leading `~/' or a bare `~' is *always* translated to the value of
321     $HOME or the home directory of the current user, regardless of any
322     preexpansion hook. */
323  if (filename[1] == '\0' || filename[1] == '/')
324    {
325      /* Prefix $HOME to the rest of the string. */
326      expansion = sh_get_env_value ("HOME");
327
328      /* If there is no HOME variable, look up the directory in
329         the password database. */
330      if (expansion == 0)
331        expansion = sh_get_home_dir ();
332
333      return (glue_prefix_and_suffix (expansion, filename, 1));
334    }
335
336  username = isolate_tilde_prefix (filename, &user_len);
337
338  if (tilde_expansion_preexpansion_hook)
339    {
340      expansion = (*tilde_expansion_preexpansion_hook) (username);
341      if (expansion)
342        {
343          dirname = glue_prefix_and_suffix (expansion, filename, user_len);
344          free (username);
345          free (expansion);
346          return (dirname);
347        }
348    }
349
350  /* No preexpansion hook, or the preexpansion hook failed.  Look in the
351     password database. */
352  dirname = (char *)NULL;
353  user_entry = getpwnam (username);
354  if (user_entry == 0)
355    {
356      /* If the calling program has a special syntax for expanding tildes,
357         and we couldn't find a standard expansion, then let them try. */
358      if (tilde_expansion_failure_hook)
359        {
360          expansion = (*tilde_expansion_failure_hook) (username);
361          if (expansion)
362            {
363              dirname = glue_prefix_and_suffix (expansion, filename, user_len);
364              free (expansion);
365            }
366        }
367      free (username);
368      /* If we don't have a failure hook, or if the failure hook did not
369         expand the tilde, return a copy of what we were passed. */
370      if (dirname == 0)
371        dirname = savestring (filename);
372    }
373  else
374    {
375      free (username);
376      dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
377    }
378
379  endpwent ();
380  return (dirname);
381}
382
383
384#if defined (TEST)
385#undef NULL
386#include <stdio.h>
387
388main (argc, argv)
389     int argc;
390     char **argv;
391{
392  char *result, line[512];
393  int done = 0;
394
395  while (!done)
396    {
397      printf ("~expand: ");
398      fflush (stdout);
399
400      if (!gets (line))
401        strcpy (line, "done");
402
403      if ((strcmp (line, "done") == 0) ||
404          (strcmp (line, "quit") == 0) ||
405          (strcmp (line, "exit") == 0))
406        {
407          done = 1;
408          break;
409        }
410
411      result = tilde_expand (line);
412      printf ("  --> %s\n", result);
413      free (result);
414    }
415  exit (0);
416}
417
418static void memory_error_and_abort ();
419
420static void *
421xmalloc (bytes)
422     size_t bytes;
423{
424  void *temp = (char *)malloc (bytes);
425
426  if (!temp)
427    memory_error_and_abort ();
428  return (temp);
429}
430
431static void *
432xrealloc (pointer, bytes)
433     void *pointer;
434     int bytes;
435{
436  void *temp;
437
438  if (!pointer)
439    temp = malloc (bytes);
440  else
441    temp = realloc (pointer, bytes);
442
443  if (!temp)
444    memory_error_and_abort ();
445
446  return (temp);
447}
448
449static void
450memory_error_and_abort ()
451{
452  fprintf (stderr, "readline: out of virtual memory\n");
453  abort ();
454}
455
456/*
457 * Local variables:
458 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
459 * end:
460 */
461#endif /* TEST */
Note: See TracBrowser for help on using the repository browser.