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