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