121308Sache/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
221308Sache
321308Sache/* Copyright (C) 1988,1989 Free Software Foundation, Inc.
421308Sache
521308Sache   This file is part of GNU Readline, a library for reading lines
621308Sache   of text with interactive input and history editing.
721308Sache
821308Sache   Readline is free software; you can redistribute it and/or modify it
921308Sache   under the terms of the GNU General Public License as published by the
1058310Sache   Free Software Foundation; either version 2, or (at your option) any
1121308Sache   later version.
1221308Sache
1321308Sache   Readline is distributed in the hope that it will be useful, but
1421308Sache   WITHOUT ANY WARRANTY; without even the implied warranty of
1521308Sache   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1621308Sache   General Public License for more details.
1721308Sache
1821308Sache   You should have received a copy of the GNU General Public License
1921308Sache   along with Readline; see the file COPYING.  If not, write to the Free
2058310Sache   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
2121308Sache
2221308Sache#if defined (HAVE_CONFIG_H)
2321308Sache#  include <config.h>
2421308Sache#endif
2521308Sache
2626497Sache#if defined (HAVE_UNISTD_H)
2735486Sache#  ifdef _MINIX
2835486Sache#    include <sys/types.h>
2935486Sache#  endif
3026497Sache#  include <unistd.h>
3126497Sache#endif
3226497Sache
3321308Sache#if defined (HAVE_STRING_H)
3421308Sache#  include <string.h>
3521308Sache#else /* !HAVE_STRING_H */
3621308Sache#  include <strings.h>
3721308Sache#endif /* !HAVE_STRING_H */
3821308Sache
3921308Sache#if defined (HAVE_STDLIB_H)
4021308Sache#  include <stdlib.h>
4121308Sache#else
4221308Sache#  include "ansi_stdlib.h"
4321308Sache#endif /* HAVE_STDLIB_H */
4421308Sache
4521308Sache#include <sys/types.h>
46157184Sache#if defined (HAVE_PWD_H)
4721308Sache#include <pwd.h>
48157184Sache#endif
4921308Sache
5021308Sache#include "tilde.h"
5121308Sache
5258310Sache#if defined (TEST) || defined (STATIC_MALLOC)
53119610Sachestatic void *xmalloc (), *xrealloc ();
5458310Sache#else
55119610Sache#  include "xmalloc.h"
5658310Sache#endif /* TEST || STATIC_MALLOC */
5758310Sache
5821308Sache#if !defined (HAVE_GETPW_DECLS)
59157184Sache#  if defined (HAVE_GETPWUID)
60119610Sacheextern struct passwd *getpwuid PARAMS((uid_t));
61157184Sache#  endif
62157184Sache#  if defined (HAVE_GETPWNAM)
63119610Sacheextern struct passwd *getpwnam PARAMS((const char *));
64157184Sache#  endif
6521308Sache#endif /* !HAVE_GETPW_DECLS */
6621308Sache
6721308Sache#if !defined (savestring)
68119610Sache#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
6921308Sache#endif /* !savestring */
7021308Sache
7121308Sache#if !defined (NULL)
7221308Sache#  if defined (__STDC__)
7321308Sache#    define NULL ((void *) 0)
7421308Sache#  else
7521308Sache#    define NULL 0x0
7621308Sache#  endif /* !__STDC__ */
7721308Sache#endif /* !NULL */
7821308Sache
7947558Sache/* If being compiled as part of bash, these will be satisfied from
8047558Sache   variables.o.  If being compiled as part of readline, they will
8147558Sache   be satisfied from shell.o. */
82119610Sacheextern char *sh_get_home_dir PARAMS((void));
83119610Sacheextern char *sh_get_env_value PARAMS((const char *));
8447558Sache
8521308Sache/* The default value of tilde_additional_prefixes.  This is set to
8621308Sache   whitespace preceding a tilde so that simple programs which do not
8721308Sache   perform any word separation get desired behaviour. */
8875406Sachestatic const char *default_prefixes[] =
8975406Sache  { " ~", "\t~", (const char *)NULL };
9021308Sache
9121308Sache/* The default value of tilde_additional_suffixes.  This is set to
9221308Sache   whitespace or newline so that simple programs which do not
9321308Sache   perform any word separation get desired behaviour. */
9475406Sachestatic const char *default_suffixes[] =
9575406Sache  { " ", "\n", (const char *)NULL };
9621308Sache
9726497Sache/* If non-null, this contains the address of a function that the application
9826497Sache   wants called before trying the standard tilde expansions.  The function
9926497Sache   is called with the text sans tilde, and returns a malloc()'ed string
10026497Sache   which is the expansion, or a NULL pointer if the expansion fails. */
10175406Sachetilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
10226497Sache
10321308Sache/* If non-null, this contains the address of a function to call if the
10421308Sache   standard meaning for expanding a tilde fails.  The function is called
10521308Sache   with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
10621308Sache   which is the expansion, or a NULL pointer if there is no expansion. */
10775406Sachetilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
10821308Sache
10921308Sache/* When non-null, this is a NULL terminated array of strings which
11021308Sache   are duplicates for a tilde prefix.  Bash uses this to expand
11121308Sache   `=~' and `:~'. */
11275406Sachechar **tilde_additional_prefixes = (char **)default_prefixes;
11321308Sache
11421308Sache/* When non-null, this is a NULL terminated array of strings which match
11521308Sache   the end of a username, instead of just "/".  Bash sets this to
11621308Sache   `:' and `=~'. */
11775406Sachechar **tilde_additional_suffixes = (char **)default_suffixes;
11821308Sache
119119610Sachestatic int tilde_find_prefix PARAMS((const char *, int *));
120119610Sachestatic int tilde_find_suffix PARAMS((const char *));
121119610Sachestatic char *isolate_tilde_prefix PARAMS((const char *, int *));
122119610Sachestatic char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
123119610Sache
12421308Sache/* Find the start of a tilde expansion in STRING, and return the index of
12521308Sache   the tilde which starts the expansion.  Place the length of the text
12621308Sache   which identified this tilde starter in LEN, excluding the tilde itself. */
12721308Sachestatic int
12821308Sachetilde_find_prefix (string, len)
129119610Sache     const char *string;
13021308Sache     int *len;
13121308Sache{
13221308Sache  register int i, j, string_len;
13358310Sache  register char **prefixes;
13421308Sache
13558310Sache  prefixes = tilde_additional_prefixes;
13658310Sache
13721308Sache  string_len = strlen (string);
13821308Sache  *len = 0;
13921308Sache
14026497Sache  if (*string == '\0' || *string == '~')
14121308Sache    return (0);
14221308Sache
14321308Sache  if (prefixes)
14421308Sache    {
14521308Sache      for (i = 0; i < string_len; i++)
14621308Sache	{
14721308Sache	  for (j = 0; prefixes[j]; j++)
14821308Sache	    {
14921308Sache	      if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
15021308Sache		{
15121308Sache		  *len = strlen (prefixes[j]) - 1;
15221308Sache		  return (i + *len);
15321308Sache		}
15421308Sache	    }
15521308Sache	}
15621308Sache    }
15721308Sache  return (string_len);
15821308Sache}
15921308Sache
16021308Sache/* Find the end of a tilde expansion in STRING, and return the index of
16121308Sache   the character which ends the tilde definition.  */
16221308Sachestatic int
16321308Sachetilde_find_suffix (string)
164119610Sache     const char *string;
16521308Sache{
16621308Sache  register int i, j, string_len;
16726497Sache  register char **suffixes;
16821308Sache
16926497Sache  suffixes = tilde_additional_suffixes;
17021308Sache  string_len = strlen (string);
17121308Sache
17221308Sache  for (i = 0; i < string_len; i++)
17321308Sache    {
17458310Sache#if defined (__MSDOS__)
17558310Sache      if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
17658310Sache#else
17726497Sache      if (string[i] == '/' /* || !string[i] */)
17858310Sache#endif
17921308Sache	break;
18021308Sache
18121308Sache      for (j = 0; suffixes && suffixes[j]; j++)
18221308Sache	{
18321308Sache	  if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
18421308Sache	    return (i);
18521308Sache	}
18621308Sache    }
18721308Sache  return (i);
18821308Sache}
18921308Sache
19021308Sache/* Return a new string which is the result of tilde expanding STRING. */
19121308Sachechar *
19221308Sachetilde_expand (string)
19375406Sache     const char *string;
19421308Sache{
19526497Sache  char *result;
19621308Sache  int result_size, result_index;
19721308Sache
19826497Sache  result_index = result_size = 0;
19926497Sache  if (result = strchr (string, '~'))
200119610Sache    result = (char *)xmalloc (result_size = (strlen (string) + 16));
20126497Sache  else
202119610Sache    result = (char *)xmalloc (result_size = (strlen (string) + 1));
20321308Sache
20421308Sache  /* Scan through STRING expanding tildes as we come to them. */
20521308Sache  while (1)
20621308Sache    {
20721308Sache      register int start, end;
20821308Sache      char *tilde_word, *expansion;
20921308Sache      int len;
21021308Sache
21121308Sache      /* Make START point to the tilde which starts the expansion. */
21221308Sache      start = tilde_find_prefix (string, &len);
21321308Sache
21421308Sache      /* Copy the skipped text into the result. */
21521308Sache      if ((result_index + start + 1) > result_size)
216119610Sache	result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
21721308Sache
21821308Sache      strncpy (result + result_index, string, start);
21921308Sache      result_index += start;
22021308Sache
22121308Sache      /* Advance STRING to the starting tilde. */
22221308Sache      string += start;
22321308Sache
22421308Sache      /* Make END be the index of one after the last character of the
22521308Sache	 username. */
22621308Sache      end = tilde_find_suffix (string);
22721308Sache
22821308Sache      /* If both START and END are zero, we are all done. */
22921308Sache      if (!start && !end)
23021308Sache	break;
23121308Sache
23221308Sache      /* Expand the entire tilde word, and copy it into RESULT. */
233119610Sache      tilde_word = (char *)xmalloc (1 + end);
23421308Sache      strncpy (tilde_word, string, end);
23521308Sache      tilde_word[end] = '\0';
23621308Sache      string += end;
23721308Sache
23821308Sache      expansion = tilde_expand_word (tilde_word);
23921308Sache      free (tilde_word);
24021308Sache
24121308Sache      len = strlen (expansion);
24275406Sache#ifdef __CYGWIN__
24358310Sache      /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
24475406Sache	 $HOME for `user' is /.  On cygwin, // denotes a network drive. */
24558310Sache      if (len > 1 || *expansion != '/' || *string != '/')
24658310Sache#endif
24758310Sache	{
24858310Sache	  if ((result_index + len + 1) > result_size)
249119610Sache	    result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
25021308Sache
25158310Sache	  strcpy (result + result_index, expansion);
25258310Sache	  result_index += len;
25358310Sache	}
25421308Sache      free (expansion);
25521308Sache    }
25621308Sache
25721308Sache  result[result_index] = '\0';
25821308Sache
25921308Sache  return (result);
26021308Sache}
26121308Sache
26226497Sache/* Take FNAME and return the tilde prefix we want expanded.  If LENP is
26326497Sache   non-null, the index of the end of the prefix into FNAME is returned in
26426497Sache   the location it points to. */
26526497Sachestatic char *
26626497Sacheisolate_tilde_prefix (fname, lenp)
267119610Sache     const char *fname;
26826497Sache     int *lenp;
26926497Sache{
27026497Sache  char *ret;
27126497Sache  int i;
27226497Sache
273119610Sache  ret = (char *)xmalloc (strlen (fname));
27458310Sache#if defined (__MSDOS__)
27558310Sache  for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
27658310Sache#else
27726497Sache  for (i = 1; fname[i] && fname[i] != '/'; i++)
27858310Sache#endif
27926497Sache    ret[i - 1] = fname[i];
28026497Sache  ret[i - 1] = '\0';
28126497Sache  if (lenp)
28226497Sache    *lenp = i;
28326497Sache  return ret;
28426497Sache}
28526497Sache
286157184Sache#if 0
287157184Sache/* Public function to scan a string (FNAME) beginning with a tilde and find
288157184Sache   the portion of the string that should be passed to the tilde expansion
289157184Sache   function.  Right now, it just calls tilde_find_suffix and allocates new
290157184Sache   memory, but it can be expanded to do different things later. */
291157184Sachechar *
292157184Sachetilde_find_word (fname, flags, lenp)
293157184Sache     const char *fname;
294157184Sache     int flags, *lenp;
295157184Sache{
296157184Sache  int x;
297157184Sache  char *r;
298157184Sache
299157184Sache  x = tilde_find_suffix (fname);
300157184Sache  if (x == 0)
301157184Sache    {
302157184Sache      r = savestring (fname);
303157184Sache      if (lenp)
304157184Sache	*lenp = 0;
305157184Sache    }
306157184Sache  else
307157184Sache    {
308157184Sache      r = (char *)xmalloc (1 + x);
309157184Sache      strncpy (r, fname, x);
310157184Sache      r[x] = '\0';
311157184Sache      if (lenp)
312157184Sache	*lenp = x;
313157184Sache    }
314157184Sache
315157184Sache  return r;
316157184Sache}
317157184Sache#endif
318157184Sache
31926497Sache/* Return a string that is PREFIX concatenated with SUFFIX starting at
32026497Sache   SUFFIND. */
32126497Sachestatic char *
32226497Sacheglue_prefix_and_suffix (prefix, suffix, suffind)
323119610Sache     char *prefix;
324119610Sache     const char *suffix;
32526497Sache     int suffind;
32626497Sache{
32726497Sache  char *ret;
32826497Sache  int plen, slen;
32926497Sache
33026497Sache  plen = (prefix && *prefix) ? strlen (prefix) : 0;
33126497Sache  slen = strlen (suffix + suffind);
332119610Sache  ret = (char *)xmalloc (plen + slen + 1);
33358310Sache  if (plen)
33426497Sache    strcpy (ret, prefix);
33526497Sache  strcpy (ret + plen, suffix + suffind);
33626497Sache  return ret;
33726497Sache}
33826497Sache
33921308Sache/* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
34026497Sache   tilde.  If there is no expansion, call tilde_expansion_failure_hook.
34126497Sache   This always returns a newly-allocated string, never static storage. */
34221308Sachechar *
34321308Sachetilde_expand_word (filename)
34475406Sache     const char *filename;
34521308Sache{
34626497Sache  char *dirname, *expansion, *username;
34726497Sache  int user_len;
34826497Sache  struct passwd *user_entry;
34921308Sache
35026497Sache  if (filename == 0)
35121308Sache    return ((char *)NULL);
35221308Sache
35326497Sache  if (*filename != '~')
35426497Sache    return (savestring (filename));
35521308Sache
35626497Sache  /* A leading `~/' or a bare `~' is *always* translated to the value of
35726497Sache     $HOME or the home directory of the current user, regardless of any
35826497Sache     preexpansion hook. */
35926497Sache  if (filename[1] == '\0' || filename[1] == '/')
36021308Sache    {
36126497Sache      /* Prefix $HOME to the rest of the string. */
36275406Sache      expansion = sh_get_env_value ("HOME");
36321308Sache
36421308Sache      /* If there is no HOME variable, look up the directory in
36521308Sache	 the password database. */
36626497Sache      if (expansion == 0)
36775406Sache	expansion = sh_get_home_dir ();
36821308Sache
36926497Sache      return (glue_prefix_and_suffix (expansion, filename, 1));
37021308Sache    }
37121308Sache
37226497Sache  username = isolate_tilde_prefix (filename, &user_len);
37321308Sache
37426497Sache  if (tilde_expansion_preexpansion_hook)
37526497Sache    {
37626497Sache      expansion = (*tilde_expansion_preexpansion_hook) (username);
37726497Sache      if (expansion)
37821308Sache	{
37926497Sache	  dirname = glue_prefix_and_suffix (expansion, filename, user_len);
38026497Sache	  free (username);
38126497Sache	  free (expansion);
38226497Sache	  return (dirname);
38326497Sache	}
38426497Sache    }
38526497Sache
38626497Sache  /* No preexpansion hook, or the preexpansion hook failed.  Look in the
38726497Sache     password database. */
38826497Sache  dirname = (char *)NULL;
389157184Sache#if defined (HAVE_GETPWNAM)
39026497Sache  user_entry = getpwnam (username);
391157184Sache#else
392157184Sache  user_entry = 0;
393157184Sache#endif
39426497Sache  if (user_entry == 0)
39526497Sache    {
39626497Sache      /* If the calling program has a special syntax for expanding tildes,
39726497Sache	 and we couldn't find a standard expansion, then let them try. */
39826497Sache      if (tilde_expansion_failure_hook)
39926497Sache	{
40026497Sache	  expansion = (*tilde_expansion_failure_hook) (username);
40126497Sache	  if (expansion)
40221308Sache	    {
40326497Sache	      dirname = glue_prefix_and_suffix (expansion, filename, user_len);
40426497Sache	      free (expansion);
40521308Sache	    }
40621308Sache	}
40726497Sache      /* If we don't have a failure hook, or if the failure hook did not
40826497Sache	 expand the tilde, return a copy of what we were passed. */
40926497Sache      if (dirname == 0)
41026497Sache	dirname = savestring (filename);
41121308Sache    }
412165670Sache#if defined (HAVE_GETPWENT)
41326497Sache  else
414165670Sache    dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
415165670Sache#endif
416165670Sache
417165670Sache  free (username);
418157184Sache#if defined (HAVE_GETPWENT)
41926497Sache  endpwent ();
420157184Sache#endif
42121308Sache  return (dirname);
42221308Sache}
42321308Sache
42421308Sache
42521308Sache#if defined (TEST)
42621308Sache#undef NULL
42721308Sache#include <stdio.h>
42821308Sache
42921308Sachemain (argc, argv)
43021308Sache     int argc;
43121308Sache     char **argv;
43221308Sache{
43321308Sache  char *result, line[512];
43421308Sache  int done = 0;
43521308Sache
43621308Sache  while (!done)
43721308Sache    {
43821308Sache      printf ("~expand: ");
43921308Sache      fflush (stdout);
44021308Sache
44121308Sache      if (!gets (line))
44221308Sache	strcpy (line, "done");
44321308Sache
44421308Sache      if ((strcmp (line, "done") == 0) ||
44521308Sache	  (strcmp (line, "quit") == 0) ||
44621308Sache	  (strcmp (line, "exit") == 0))
44721308Sache	{
44821308Sache	  done = 1;
44921308Sache	  break;
45021308Sache	}
45121308Sache
45221308Sache      result = tilde_expand (line);
45321308Sache      printf ("  --> %s\n", result);
45421308Sache      free (result);
45521308Sache    }
45621308Sache  exit (0);
45721308Sache}
45821308Sache
45921308Sachestatic void memory_error_and_abort ();
46021308Sache
461119610Sachestatic void *
46221308Sachexmalloc (bytes)
463119610Sache     size_t bytes;
46421308Sache{
465119610Sache  void *temp = (char *)malloc (bytes);
46621308Sache
46721308Sache  if (!temp)
46821308Sache    memory_error_and_abort ();
46921308Sache  return (temp);
47021308Sache}
47121308Sache
472119610Sachestatic void *
47321308Sachexrealloc (pointer, bytes)
474119610Sache     void *pointer;
47521308Sache     int bytes;
47621308Sache{
477119610Sache  void *temp;
47821308Sache
47921308Sache  if (!pointer)
480119610Sache    temp = malloc (bytes);
48121308Sache  else
482119610Sache    temp = realloc (pointer, bytes);
48321308Sache
48421308Sache  if (!temp)
48521308Sache    memory_error_and_abort ();
48621308Sache
48721308Sache  return (temp);
48821308Sache}
48921308Sache
49021308Sachestatic void
49121308Sachememory_error_and_abort ()
49221308Sache{
49321308Sache  fprintf (stderr, "readline: out of virtual memory\n");
49421308Sache  abort ();
49521308Sache}
49621308Sache
49721308Sache/*
49821308Sache * Local variables:
49921308Sache * compile-command: "gcc -g -DTEST -o tilde tilde.c"
50021308Sache * end:
50121308Sache */
50221308Sache#endif /* TEST */
503