xref: /openbsd-src/gnu/usr.bin/texinfo/info/tilde.c (revision 1076333c323c9f213f0d653fc52002328f47dbe9)
1672dff93Sespie /* tilde.c -- tilde expansion code (~/foo := $HOME/foo).
2*1076333cSespie    $Id: tilde.c,v 1.4 2006/07/17 16:12:36 espie Exp $
3fbc94a17Sniklas 
4*1076333cSespie    Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1996, 1998, 1999,
5*1076333cSespie    2002, 2004 Free Software Foundation, Inc.
6fbc94a17Sniklas 
7fbc94a17Sniklas    This program is free software; you can redistribute it and/or modify
8fbc94a17Sniklas    it under the terms of the GNU General Public License as published by
9fbc94a17Sniklas    the Free Software Foundation; either version 2, or (at your option)
10fbc94a17Sniklas    any later version.
11fbc94a17Sniklas 
12fbc94a17Sniklas    This program is distributed in the hope that it will be useful,
13fbc94a17Sniklas    but WITHOUT ANY WARRANTY; without even the implied warranty of
14fbc94a17Sniklas    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15fbc94a17Sniklas    GNU General Public License for more details.
16fbc94a17Sniklas 
17fbc94a17Sniklas    You should have received a copy of the GNU General Public License
18fbc94a17Sniklas    along with this program; if not, write to the Free Software
19fbc94a17Sniklas    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20fbc94a17Sniklas 
21fbc94a17Sniklas    Written by Brian Fox (bfox@ai.mit.edu). */
22fbc94a17Sniklas 
23840175f0Skstailey /* Include config.h before doing alloca.  */
24840175f0Skstailey #include "info.h"
25*1076333cSespie #include "tilde.h"
26840175f0Skstailey 
27fbc94a17Sniklas #if defined (TEST) || defined (STATIC_MALLOC)
28fbc94a17Sniklas static void *xmalloc (), *xrealloc ();
29fbc94a17Sniklas #endif /* TEST || STATIC_MALLOC */
30fbc94a17Sniklas 
31fbc94a17Sniklas /* The default value of tilde_additional_prefixes.  This is set to
32fbc94a17Sniklas    whitespace preceding a tilde so that simple programs which do not
33fbc94a17Sniklas    perform any word separation get desired behaviour. */
34fbc94a17Sniklas static char *default_prefixes[] =
35fbc94a17Sniklas   { " ~", "\t~", (char *)NULL };
36fbc94a17Sniklas 
37fbc94a17Sniklas /* The default value of tilde_additional_suffixes.  This is set to
38fbc94a17Sniklas    whitespace or newline so that simple programs which do not
39fbc94a17Sniklas    perform any word separation get desired behaviour. */
40fbc94a17Sniklas static char *default_suffixes[] =
41fbc94a17Sniklas   { " ", "\n", (char *)NULL };
42fbc94a17Sniklas 
43fbc94a17Sniklas /* If non-null, this contains the address of a function to call if the
44fbc94a17Sniklas    standard meaning for expanding a tilde fails.  The function is called
45fbc94a17Sniklas    with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
46fbc94a17Sniklas    which is the expansion, or a NULL pointer if there is no expansion. */
47fbc94a17Sniklas CFunction *tilde_expansion_failure_hook = (CFunction *)NULL;
48fbc94a17Sniklas 
49fbc94a17Sniklas /* When non-null, this is a NULL terminated array of strings which
50fbc94a17Sniklas    are duplicates for a tilde prefix.  Bash uses this to expand
51fbc94a17Sniklas    `=~' and `:~'. */
52fbc94a17Sniklas char **tilde_additional_prefixes = default_prefixes;
53fbc94a17Sniklas 
54fbc94a17Sniklas /* When non-null, this is a NULL terminated array of strings which match
55fbc94a17Sniklas    the end of a username, instead of just "/".  Bash sets this to
56fbc94a17Sniklas    `:' and `=~'. */
57fbc94a17Sniklas char **tilde_additional_suffixes = default_suffixes;
58fbc94a17Sniklas 
59fbc94a17Sniklas /* Find the start of a tilde expansion in STRING, and return the index of
60fbc94a17Sniklas    the tilde which starts the expansion.  Place the length of the text
61fbc94a17Sniklas    which identified this tilde starter in LEN, excluding the tilde itself. */
62fbc94a17Sniklas static int
tilde_find_prefix(char * string,int * len)63*1076333cSespie tilde_find_prefix (char *string, int *len)
64fbc94a17Sniklas {
65fbc94a17Sniklas   register int i, j, string_len;
66fbc94a17Sniklas   register char **prefixes = tilde_additional_prefixes;
67fbc94a17Sniklas 
68fbc94a17Sniklas   string_len = strlen (string);
69fbc94a17Sniklas   *len = 0;
70fbc94a17Sniklas 
71fbc94a17Sniklas   if (!*string || *string == '~')
72fbc94a17Sniklas     return (0);
73fbc94a17Sniklas 
74fbc94a17Sniklas   if (prefixes)
75fbc94a17Sniklas     {
76fbc94a17Sniklas       for (i = 0; i < string_len; i++)
77fbc94a17Sniklas         {
78fbc94a17Sniklas           for (j = 0; prefixes[j]; j++)
79fbc94a17Sniklas             {
80fbc94a17Sniklas               if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
81fbc94a17Sniklas                 {
82fbc94a17Sniklas                   *len = strlen (prefixes[j]) - 1;
83fbc94a17Sniklas                   return (i + *len);
84fbc94a17Sniklas                 }
85fbc94a17Sniklas             }
86fbc94a17Sniklas         }
87fbc94a17Sniklas     }
88fbc94a17Sniklas   return (string_len);
89fbc94a17Sniklas }
90fbc94a17Sniklas 
91fbc94a17Sniklas /* Find the end of a tilde expansion in STRING, and return the index of
92fbc94a17Sniklas    the character which ends the tilde definition.  */
93fbc94a17Sniklas static int
tilde_find_suffix(char * string)94*1076333cSespie tilde_find_suffix (char *string)
95fbc94a17Sniklas {
96fbc94a17Sniklas   register int i, j, string_len;
97fbc94a17Sniklas   register char **suffixes = tilde_additional_suffixes;
98fbc94a17Sniklas 
99fbc94a17Sniklas   string_len = strlen (string);
100fbc94a17Sniklas 
101fbc94a17Sniklas   for (i = 0; i < string_len; i++)
102fbc94a17Sniklas     {
103672dff93Sespie       if (IS_SLASH (string[i]) || !string[i])
104fbc94a17Sniklas         break;
105fbc94a17Sniklas 
106fbc94a17Sniklas       for (j = 0; suffixes && suffixes[j]; j++)
107fbc94a17Sniklas         {
108fbc94a17Sniklas           if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
109fbc94a17Sniklas             return (i);
110fbc94a17Sniklas         }
111fbc94a17Sniklas     }
112fbc94a17Sniklas   return (i);
113fbc94a17Sniklas }
114fbc94a17Sniklas 
115fbc94a17Sniklas /* Return a new string which is the result of tilde expanding STRING. */
116fbc94a17Sniklas char *
tilde_expand(char * string)117*1076333cSespie tilde_expand (char *string)
118fbc94a17Sniklas {
119*1076333cSespie   char *result;
120fbc94a17Sniklas   int result_size, result_index;
121fbc94a17Sniklas 
122fbc94a17Sniklas   result_size = result_index = 0;
123fbc94a17Sniklas   result = (char *)NULL;
124fbc94a17Sniklas 
125fbc94a17Sniklas   /* Scan through STRING expanding tildes as we come to them. */
126fbc94a17Sniklas   while (1)
127fbc94a17Sniklas     {
128fbc94a17Sniklas       register int start, end;
129fbc94a17Sniklas       char *tilde_word, *expansion;
130fbc94a17Sniklas       int len;
131fbc94a17Sniklas 
132fbc94a17Sniklas       /* Make START point to the tilde which starts the expansion. */
133fbc94a17Sniklas       start = tilde_find_prefix (string, &len);
134fbc94a17Sniklas 
135fbc94a17Sniklas       /* Copy the skipped text into the result. */
136fbc94a17Sniklas       if ((result_index + start + 1) > result_size)
137fbc94a17Sniklas         result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
138fbc94a17Sniklas 
139fbc94a17Sniklas       strncpy (result + result_index, string, start);
140fbc94a17Sniklas       result_index += start;
141fbc94a17Sniklas 
142fbc94a17Sniklas       /* Advance STRING to the starting tilde. */
143fbc94a17Sniklas       string += start;
144fbc94a17Sniklas 
145fbc94a17Sniklas       /* Make END be the index of one after the last character of the
146fbc94a17Sniklas          username. */
147fbc94a17Sniklas       end = tilde_find_suffix (string);
148fbc94a17Sniklas 
149fbc94a17Sniklas       /* If both START and END are zero, we are all done. */
150fbc94a17Sniklas       if (!start && !end)
151fbc94a17Sniklas         break;
152fbc94a17Sniklas 
153fbc94a17Sniklas       /* Expand the entire tilde word, and copy it into RESULT. */
154fbc94a17Sniklas       tilde_word = (char *)xmalloc (1 + end);
155fbc94a17Sniklas       strncpy (tilde_word, string, end);
156fbc94a17Sniklas       tilde_word[end] = '\0';
157fbc94a17Sniklas       string += end;
158fbc94a17Sniklas 
159fbc94a17Sniklas       expansion = tilde_expand_word (tilde_word);
160fbc94a17Sniklas       free (tilde_word);
161fbc94a17Sniklas 
162fbc94a17Sniklas       len = strlen (expansion);
163fbc94a17Sniklas       if ((result_index + len + 1) > result_size)
164fbc94a17Sniklas         result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
165fbc94a17Sniklas 
166fbc94a17Sniklas       strcpy (result + result_index, expansion);
167fbc94a17Sniklas       result_index += len;
168fbc94a17Sniklas       free (expansion);
169fbc94a17Sniklas     }
170fbc94a17Sniklas 
171fbc94a17Sniklas   result[result_index] = '\0';
172fbc94a17Sniklas 
173fbc94a17Sniklas   return (result);
174fbc94a17Sniklas }
175fbc94a17Sniklas 
176fbc94a17Sniklas /* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
177fbc94a17Sniklas    tilde.  If there is no expansion, call tilde_expansion_failure_hook. */
178fbc94a17Sniklas char *
tilde_expand_word(char * filename)179*1076333cSespie tilde_expand_word (char *filename)
180fbc94a17Sniklas {
181672dff93Sespie   char *dirname = filename ? xstrdup (filename) : NULL;
182fbc94a17Sniklas 
183fbc94a17Sniklas   if (dirname && *dirname == '~')
184fbc94a17Sniklas     {
185fbc94a17Sniklas       char *temp_name;
186672dff93Sespie       if (!dirname[1] || IS_SLASH (dirname[1]))
187fbc94a17Sniklas         {
188fbc94a17Sniklas           /* Prepend $HOME to the rest of the string. */
18940248eceSdownsj           char *temp_home = getenv ("HOME");
190fbc94a17Sniklas 
191fbc94a17Sniklas           /* If there is no HOME variable, look up the directory in
192fbc94a17Sniklas              the password database. */
193fbc94a17Sniklas           if (!temp_home)
194fbc94a17Sniklas             {
195fbc94a17Sniklas               struct passwd *entry;
196fbc94a17Sniklas 
197fbc94a17Sniklas               entry = (struct passwd *) getpwuid (getuid ());
198fbc94a17Sniklas               if (entry)
199fbc94a17Sniklas                 temp_home = entry->pw_dir;
200fbc94a17Sniklas             }
201fbc94a17Sniklas 
202672dff93Sespie           temp_name = xmalloc (1 + strlen (&dirname[1])
203fbc94a17Sniklas                                + (temp_home ? strlen (temp_home) : 0));
204fbc94a17Sniklas           if (temp_home)
205fbc94a17Sniklas             strcpy (temp_name, temp_home);
206672dff93Sespie           else
207672dff93Sespie             temp_name[0] = 0;
208fbc94a17Sniklas           strcat (temp_name, &dirname[1]);
209fbc94a17Sniklas           free (dirname);
210840175f0Skstailey           dirname = xstrdup (temp_name);
211672dff93Sespie           free (temp_name);
212fbc94a17Sniklas         }
213fbc94a17Sniklas       else
214fbc94a17Sniklas         {
215fbc94a17Sniklas           struct passwd *user_entry;
216672dff93Sespie           char *username = xmalloc (257);
217fbc94a17Sniklas           int i, c;
218fbc94a17Sniklas 
219840175f0Skstailey           for (i = 1; (c = dirname[i]); i++)
220fbc94a17Sniklas             {
221672dff93Sespie               if (IS_SLASH (c))
222fbc94a17Sniklas                 break;
223fbc94a17Sniklas               else
224fbc94a17Sniklas                 username[i - 1] = c;
225fbc94a17Sniklas             }
226672dff93Sespie           username[i - 1] = 0;
227fbc94a17Sniklas 
228fbc94a17Sniklas           if (!(user_entry = (struct passwd *) getpwnam (username)))
229fbc94a17Sniklas             {
230fbc94a17Sniklas               /* If the calling program has a special syntax for
231fbc94a17Sniklas                  expanding tildes, and we couldn't find a standard
232fbc94a17Sniklas                  expansion, then let them try. */
233fbc94a17Sniklas               if (tilde_expansion_failure_hook)
234fbc94a17Sniklas                 {
235*1076333cSespie                   char *expansion = (*tilde_expansion_failure_hook) (username);
236fbc94a17Sniklas 
237fbc94a17Sniklas                   if (expansion)
238fbc94a17Sniklas                     {
239672dff93Sespie                       temp_name = xmalloc (1 + strlen (expansion)
240672dff93Sespie                                            + strlen (&dirname[i]));
241fbc94a17Sniklas                       strcpy (temp_name, expansion);
242fbc94a17Sniklas                       strcat (temp_name, &dirname[i]);
243fbc94a17Sniklas                       free (expansion);
244fbc94a17Sniklas                       goto return_name;
245fbc94a17Sniklas                     }
246fbc94a17Sniklas                 }
247fbc94a17Sniklas               /* We shouldn't report errors. */
248fbc94a17Sniklas             }
249fbc94a17Sniklas           else
250fbc94a17Sniklas             {
251672dff93Sespie               temp_name = xmalloc (1 + strlen (user_entry->pw_dir)
252672dff93Sespie                                    + strlen (&dirname[i]));
253fbc94a17Sniklas               strcpy (temp_name, user_entry->pw_dir);
254fbc94a17Sniklas               strcat (temp_name, &dirname[i]);
255672dff93Sespie 
256fbc94a17Sniklas             return_name:
257fbc94a17Sniklas               free (dirname);
258840175f0Skstailey               dirname = xstrdup (temp_name);
259672dff93Sespie               free (temp_name);
260fbc94a17Sniklas             }
261672dff93Sespie 
262fbc94a17Sniklas           endpwent ();
263672dff93Sespie           free (username);
264fbc94a17Sniklas         }
265fbc94a17Sniklas     }
266672dff93Sespie   return dirname;
267fbc94a17Sniklas }
268fbc94a17Sniklas 
269fbc94a17Sniklas 
270fbc94a17Sniklas #if defined (TEST)
271fbc94a17Sniklas #undef NULL
272fbc94a17Sniklas #include <stdio.h>
273fbc94a17Sniklas 
main(argc,argv)274fbc94a17Sniklas main (argc, argv)
275fbc94a17Sniklas      int argc;
276fbc94a17Sniklas      char **argv;
277fbc94a17Sniklas {
278fbc94a17Sniklas   char *result, line[512];
279fbc94a17Sniklas   int done = 0;
280fbc94a17Sniklas 
281fbc94a17Sniklas   while (!done)
282fbc94a17Sniklas     {
283fbc94a17Sniklas       printf ("~expand: ");
284fbc94a17Sniklas       fflush (stdout);
285fbc94a17Sniklas 
286fbc94a17Sniklas       if (!gets (line))
287fbc94a17Sniklas         strcpy (line, "done");
288fbc94a17Sniklas 
289fbc94a17Sniklas       if ((strcmp (line, "done") == 0) ||
290fbc94a17Sniklas           (strcmp (line, "quit") == 0) ||
291fbc94a17Sniklas           (strcmp (line, "exit") == 0))
292fbc94a17Sniklas         {
293fbc94a17Sniklas           done = 1;
294fbc94a17Sniklas           break;
295fbc94a17Sniklas         }
296fbc94a17Sniklas 
297fbc94a17Sniklas       result = tilde_expand (line);
298fbc94a17Sniklas       printf ("  --> %s\n", result);
299fbc94a17Sniklas       free (result);
300fbc94a17Sniklas     }
301672dff93Sespie   xexit (0);
302fbc94a17Sniklas }
303fbc94a17Sniklas 
304fbc94a17Sniklas static void memory_error_and_abort ();
305fbc94a17Sniklas 
306fbc94a17Sniklas static void *
xmalloc(bytes)307fbc94a17Sniklas xmalloc (bytes)
308fbc94a17Sniklas      int bytes;
309fbc94a17Sniklas {
310fbc94a17Sniklas   void *temp = (void *)malloc (bytes);
311fbc94a17Sniklas 
312fbc94a17Sniklas   if (!temp)
313fbc94a17Sniklas     memory_error_and_abort ();
314fbc94a17Sniklas   return (temp);
315fbc94a17Sniklas }
316fbc94a17Sniklas 
317fbc94a17Sniklas static void *
xrealloc(pointer,bytes)318fbc94a17Sniklas xrealloc (pointer, bytes)
319fbc94a17Sniklas      void *pointer;
320fbc94a17Sniklas      int bytes;
321fbc94a17Sniklas {
322fbc94a17Sniklas   void *temp;
323fbc94a17Sniklas 
324fbc94a17Sniklas   if (!pointer)
325fbc94a17Sniklas     temp = (char *)malloc (bytes);
326fbc94a17Sniklas   else
327fbc94a17Sniklas     temp = (char *)realloc (pointer, bytes);
328fbc94a17Sniklas 
329fbc94a17Sniklas   if (!temp)
330fbc94a17Sniklas     memory_error_and_abort ();
331fbc94a17Sniklas 
332fbc94a17Sniklas   return (temp);
333fbc94a17Sniklas }
334fbc94a17Sniklas 
335fbc94a17Sniklas static void
memory_error_and_abort()336fbc94a17Sniklas memory_error_and_abort ()
337fbc94a17Sniklas {
338840175f0Skstailey   fprintf (stderr, _("readline: Out of virtual memory!\n"));
339fbc94a17Sniklas   abort ();
340fbc94a17Sniklas }
341fbc94a17Sniklas #endif /* TEST */
342fbc94a17Sniklas 
343