* ld.texinfo (Simple Example): Rewrite a few things as suggested
[deliverable/binutils-gdb.git] / readline / tilde.c
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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
21
22 #if defined (__GNUC__)
23 # undef alloca
24 # define alloca __builtin_alloca
25 #else /* !__GNUC__ */
26 # if defined (_AIX)
27 #pragma alloca
28 # else /* !_AIX */
29 # if defined (HAVE_ALLOCA_H)
30 # include <alloca.h>
31 # endif /* HAVE_ALLOCA_H */
32 # endif /* !AIX */
33 #endif /* !__GNUC__ */
34
35 #if defined (HAVE_STRING_H)
36 # include <string.h>
37 #else /* !HAVE_STRING_H */
38 # include <strings.h>
39 #endif /* !HAVE_STRING_H */
40
41 #if defined (HAVE_STDLIB_H)
42 # include <stdlib.h>
43 #else
44 # include "ansi_stdlib.h"
45 #endif /* HAVE_STDLIB_H */
46
47 #include <tilde/tilde.h>
48 #include <pwd.h>
49
50 #if !defined (sgi) && !defined (isc386)
51 extern struct passwd *getpwnam (), *getpwuid ();
52 #endif /* !sgi */
53
54 #if !defined (savestring)
55 extern char *xmalloc ();
56 # ifndef strcpy
57 extern char *strcpy ();
58 # endif
59 #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
60 #endif /* !savestring */
61
62 #if !defined (NULL)
63 # if defined (__STDC__)
64 # define NULL ((void *) 0)
65 # else
66 # define NULL 0x0
67 # endif /* !__STDC__ */
68 #endif /* !NULL */
69
70 #if defined (TEST) || defined (STATIC_MALLOC)
71 static char *xmalloc (), *xrealloc ();
72 #else
73 extern char *xmalloc (), *xrealloc ();
74 #endif /* TEST || STATIC_MALLOC */
75
76 /* The default value of tilde_additional_prefixes. This is set to
77 whitespace preceding a tilde so that simple programs which do not
78 perform any word separation get desired behaviour. */
79 static char *default_prefixes[] =
80 { " ~", "\t~", (char *)NULL };
81
82 /* The default value of tilde_additional_suffixes. This is set to
83 whitespace or newline so that simple programs which do not
84 perform any word separation get desired behaviour. */
85 static char *default_suffixes[] =
86 { " ", "\n", (char *)NULL };
87
88 /* If non-null, this contains the address of a function to call if the
89 standard meaning for expanding a tilde fails. The function is called
90 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
91 which is the expansion, or a NULL pointer if there is no expansion. */
92 Function *tilde_expansion_failure_hook = (Function *)NULL;
93
94 /* When non-null, this is a NULL terminated array of strings which
95 are duplicates for a tilde prefix. Bash uses this to expand
96 `=~' and `:~'. */
97 char **tilde_additional_prefixes = default_prefixes;
98
99 /* When non-null, this is a NULL terminated array of strings which match
100 the end of a username, instead of just "/". Bash sets this to
101 `:' and `=~'. */
102 char **tilde_additional_suffixes = default_suffixes;
103
104 /* Find the start of a tilde expansion in STRING, and return the index of
105 the tilde which starts the expansion. Place the length of the text
106 which identified this tilde starter in LEN, excluding the tilde itself. */
107 static int
108 tilde_find_prefix (string, len)
109 char *string;
110 int *len;
111 {
112 register int i, j, string_len;
113 register char **prefixes = tilde_additional_prefixes;
114
115 string_len = strlen (string);
116 *len = 0;
117
118 if (!*string || *string == '~')
119 return (0);
120
121 if (prefixes)
122 {
123 for (i = 0; i < string_len; i++)
124 {
125 for (j = 0; prefixes[j]; j++)
126 {
127 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
128 {
129 *len = strlen (prefixes[j]) - 1;
130 return (i + *len);
131 }
132 }
133 }
134 }
135 return (string_len);
136 }
137
138 /* Find the end of a tilde expansion in STRING, and return the index of
139 the character which ends the tilde definition. */
140 static int
141 tilde_find_suffix (string)
142 char *string;
143 {
144 register int i, j, string_len;
145 register char **suffixes = tilde_additional_suffixes;
146
147 string_len = strlen (string);
148
149 for (i = 0; i < string_len; i++)
150 {
151 if (string[i] == '/' || !string[i])
152 break;
153
154 for (j = 0; suffixes && suffixes[j]; j++)
155 {
156 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
157 return (i);
158 }
159 }
160 return (i);
161 }
162
163 /* Return a new string which is the result of tilde expanding STRING. */
164 char *
165 tilde_expand (string)
166 char *string;
167 {
168 char *result, *tilde_expand_word ();
169 int result_size, result_index;
170
171 result_size = result_index = 0;
172 result = (char *)NULL;
173
174 /* Scan through STRING expanding tildes as we come to them. */
175 while (1)
176 {
177 register int start, end;
178 char *tilde_word, *expansion;
179 int len;
180
181 /* Make START point to the tilde which starts the expansion. */
182 start = tilde_find_prefix (string, &len);
183
184 /* Copy the skipped text into the result. */
185 if ((result_index + start + 1) > result_size)
186 result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
187
188 strncpy (result + result_index, string, start);
189 result_index += start;
190
191 /* Advance STRING to the starting tilde. */
192 string += start;
193
194 /* Make END be the index of one after the last character of the
195 username. */
196 end = tilde_find_suffix (string);
197
198 /* If both START and END are zero, we are all done. */
199 if (!start && !end)
200 break;
201
202 /* Expand the entire tilde word, and copy it into RESULT. */
203 tilde_word = (char *)xmalloc (1 + end);
204 strncpy (tilde_word, string, end);
205 tilde_word[end] = '\0';
206 string += end;
207
208 expansion = tilde_expand_word (tilde_word);
209 free (tilde_word);
210
211 len = strlen (expansion);
212 if ((result_index + len + 1) > result_size)
213 result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
214
215 strcpy (result + result_index, expansion);
216 result_index += len;
217 free (expansion);
218 }
219
220 result[result_index] = '\0';
221
222 return (result);
223 }
224
225 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
226 tilde. If there is no expansion, call tilde_expansion_failure_hook. */
227 char *
228 tilde_expand_word (filename)
229 char *filename;
230 {
231 char *dirname;
232
233 dirname = filename ? savestring (filename) : (char *)NULL;
234
235 if (dirname && *dirname == '~')
236 {
237 char *temp_name;
238 if (!dirname[1] || dirname[1] == '/')
239 {
240 /* Prepend $HOME to the rest of the string. */
241 char *temp_home = (char *)getenv ("HOME");
242
243 /* If there is no HOME variable, look up the directory in
244 the password database. */
245 if (!temp_home)
246 {
247 struct passwd *entry;
248
249 entry = getpwuid (getuid ());
250 if (entry)
251 temp_home = entry->pw_dir;
252 }
253
254 temp_name = (char *)alloca (1 + strlen (&dirname[1])
255 + (temp_home ? strlen (temp_home) : 0));
256 temp_name[0] = '\0';
257 if (temp_home)
258 strcpy (temp_name, temp_home);
259 strcat (temp_name, &dirname[1]);
260 free (dirname);
261 dirname = savestring (temp_name);
262 }
263 else
264 {
265 struct passwd *user_entry;
266 char *username = (char *)alloca (257);
267 int i, c;
268
269 for (i = 1; c = dirname[i]; i++)
270 {
271 if (c == '/')
272 break;
273 else
274 username[i - 1] = c;
275 }
276 username[i - 1] = '\0';
277
278 if (!(user_entry = getpwnam (username)))
279 {
280 /* If the calling program has a special syntax for
281 expanding tildes, and we couldn't find a standard
282 expansion, then let them try. */
283 if (tilde_expansion_failure_hook)
284 {
285 char *expansion;
286
287 expansion =
288 (char *)(*tilde_expansion_failure_hook) (username);
289
290 if (expansion)
291 {
292 temp_name = (char *)alloca (1 + strlen (expansion)
293 + strlen (&dirname[i]));
294 strcpy (temp_name, expansion);
295 strcat (temp_name, &dirname[i]);
296 free (expansion);
297 goto return_name;
298 }
299 }
300 /* We shouldn't report errors. */
301 }
302 else
303 {
304 temp_name = (char *)alloca (1 + strlen (user_entry->pw_dir)
305 + strlen (&dirname[i]));
306 strcpy (temp_name, user_entry->pw_dir);
307 strcat (temp_name, &dirname[i]);
308 return_name:
309 free (dirname);
310 dirname = savestring (temp_name);
311 }
312 endpwent ();
313 }
314 }
315 return (dirname);
316 }
317
318 \f
319 #if defined (TEST)
320 #undef NULL
321 #include <stdio.h>
322
323 main (argc, argv)
324 int argc;
325 char **argv;
326 {
327 char *result, line[512];
328 int done = 0;
329
330 while (!done)
331 {
332 printf ("~expand: ");
333 fflush (stdout);
334
335 if (!gets (line))
336 strcpy (line, "done");
337
338 if ((strcmp (line, "done") == 0) ||
339 (strcmp (line, "quit") == 0) ||
340 (strcmp (line, "exit") == 0))
341 {
342 done = 1;
343 break;
344 }
345
346 result = tilde_expand (line);
347 printf (" --> %s\n", result);
348 free (result);
349 }
350 exit (0);
351 }
352
353 static void memory_error_and_abort ();
354
355 static char *
356 xmalloc (bytes)
357 int bytes;
358 {
359 char *temp = (char *)malloc (bytes);
360
361 if (!temp)
362 memory_error_and_abort ();
363 return (temp);
364 }
365
366 static char *
367 xrealloc (pointer, bytes)
368 char *pointer;
369 int bytes;
370 {
371 char *temp;
372
373 if (!pointer)
374 temp = (char *)malloc (bytes);
375 else
376 temp = (char *)realloc (pointer, bytes);
377
378 if (!temp)
379 memory_error_and_abort ();
380
381 return (temp);
382 }
383
384 static void
385 memory_error_and_abort ()
386 {
387 fprintf (stderr, "readline: Out of virtual memory!\n");
388 abort ();
389 }
390
391 /*
392 * Local variables:
393 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
394 * end:
395 */
396 #endif /* TEST */
This page took 0.03698 seconds and 4 git commands to generate.