]>
Commit | Line | Data |
---|---|---|
ccc6cda3 JA |
1 | /* stringlib.c - Miscellaneous string functions. */ |
2 | ||
3 | /* Copyright (C) 1996 | |
4 | Free Software Foundation, Inc. | |
5 | ||
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
8 | Bash is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
10 | Software Foundation; either version 2, or (at your option) any later | |
11 | version. | |
12 | ||
13 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License along | |
19 | with Bash; see the file COPYING. If not, write to the Free Software | |
20 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
21 | ||
22 | #include "config.h" | |
23 | ||
24 | #include "bashtypes.h" | |
25 | ||
26 | #if defined (HAVE_UNISTD_H) | |
27 | # include <unistd.h> | |
28 | #endif | |
29 | ||
30 | #include "bashansi.h" | |
31 | #include <stdio.h> | |
32 | #include <ctype.h> | |
33 | ||
34 | #include "shell.h" | |
35 | ||
36 | #ifndef to_upper | |
37 | # define to_upper(c) (islower(c) ? toupper(c) : (c)) | |
38 | # define to_lower(c) (isupper(c) ? tolower(c) : (c)) | |
39 | #endif | |
40 | ||
41 | /* Convert STRING by expanding the escape sequences specified by the | |
42 | ANSI C standard. If SAWC is non-null, recognize `\c' and use that | |
43 | as a string terminator. If we see \c, set *SAWC to 1 before | |
44 | returning. LEN is the length of STRING. */ | |
45 | char * | |
46 | ansicstr (string, len, sawc) | |
47 | char *string; | |
48 | int len, *sawc; | |
49 | { | |
50 | int c; | |
51 | char *ret, *r, *s; | |
52 | ||
53 | if (string == 0 || *string == '\0') | |
54 | return ((char *)NULL); | |
55 | ||
56 | ret = xmalloc (len + 1); | |
57 | for (r = ret, s = string; s && *s; ) | |
58 | { | |
59 | c = *s++; | |
60 | if (c != '\\' || *s == '\0') | |
61 | *r++ = c; | |
62 | else | |
63 | { | |
64 | switch (c = *s++) | |
65 | { | |
66 | #if defined (__STDC__) | |
67 | case 'a': c = '\a'; break; | |
68 | case 'v': c = '\v'; break; | |
69 | #else | |
70 | case 'a': c = '\007'; break; | |
71 | case 'v': c = (int) 0x0B; break; | |
72 | #endif | |
73 | case 'b': c = '\b'; break; | |
74 | case 'e': c = '\033'; break; /* ESC -- non-ANSI */ | |
75 | case 'E': c = '\033'; break; /* ESC -- non-ANSI */ | |
76 | case 'f': c = '\f'; break; | |
77 | case 'n': c = '\n'; break; | |
78 | case 'r': c = '\r'; break; | |
79 | case 't': c = '\t'; break; | |
80 | case '0': case '1': case '2': case '3': | |
81 | case '4': case '5': case '6': case '7': | |
82 | c -= '0'; | |
83 | if (*s >= '0' && *s <= '7') | |
84 | c = c * 8 + (*s++ - '0'); | |
85 | if (*s >= '0' && *s <= '7') | |
86 | c = c * 8 + (*s++ - '0'); | |
87 | break; | |
88 | case '\\': | |
89 | case '\'': | |
90 | break; | |
91 | case 'c': | |
92 | if (sawc) | |
93 | { | |
94 | *sawc = 1; | |
95 | *r = '\0'; | |
96 | return ret; | |
97 | } | |
98 | default: *r++ = '\\'; break; | |
99 | } | |
100 | *r++ = c; | |
101 | } | |
102 | } | |
103 | *r = '\0'; | |
104 | return ret; | |
105 | } | |
106 | ||
107 | /* **************************************************************** */ | |
108 | /* */ | |
109 | /* Functions to manage arrays of strings */ | |
110 | /* */ | |
111 | /* **************************************************************** */ | |
112 | ||
113 | /* Find NAME in ARRAY. Return the index of NAME, or -1 if not present. | |
114 | ARRAY should be NULL terminated. */ | |
115 | int | |
116 | find_name_in_array (name, array) | |
117 | char *name, **array; | |
118 | { | |
119 | int i; | |
120 | ||
121 | for (i = 0; array[i]; i++) | |
122 | if (STREQ (name, array[i])) | |
123 | return (i); | |
124 | ||
125 | return (-1); | |
126 | } | |
127 | ||
128 | /* Return the length of ARRAY, a NULL terminated array of char *. */ | |
129 | int | |
130 | array_len (array) | |
131 | char **array; | |
132 | { | |
133 | register int i; | |
134 | ||
135 | for (i = 0; array[i]; i++); | |
136 | return (i); | |
137 | } | |
138 | ||
139 | /* Free the contents of ARRAY, a NULL terminated array of char *. */ | |
140 | void | |
141 | free_array_members (array) | |
142 | char **array; | |
143 | { | |
144 | register int i; | |
145 | ||
146 | if (array == 0) | |
147 | return; | |
148 | ||
149 | for (i = 0; array[i]; i++) | |
150 | free (array[i]); | |
151 | } | |
152 | ||
153 | void | |
154 | free_array (array) | |
155 | char **array; | |
156 | { | |
157 | if (array == 0) | |
158 | return; | |
159 | ||
160 | free_array_members (array); | |
161 | free (array); | |
162 | } | |
163 | ||
164 | /* Allocate and return a new copy of ARRAY and its contents. */ | |
165 | char ** | |
166 | copy_array (array) | |
167 | char **array; | |
168 | { | |
169 | register int i; | |
170 | int len; | |
171 | char **new_array; | |
172 | ||
173 | len = array_len (array); | |
174 | ||
175 | new_array = (char **)xmalloc ((len + 1) * sizeof (char *)); | |
176 | for (i = 0; array[i]; i++) | |
177 | new_array[i] = savestring (array[i]); | |
178 | new_array[i] = (char *)NULL; | |
179 | ||
180 | return (new_array); | |
181 | } | |
182 | ||
183 | /* Comparison routine for use with qsort() on arrays of strings. Uses | |
184 | strcoll(3) if available, otherwise it uses strcmp(3). */ | |
185 | int | |
186 | qsort_string_compare (s1, s2) | |
187 | register char **s1, **s2; | |
188 | { | |
189 | #if defined (HAVE_STRCOLL) | |
190 | return (strcoll (*s1, *s2)); | |
191 | #else /* !HAVE_STRCOLL */ | |
192 | int result; | |
193 | ||
194 | if ((result = **s1 - **s2) == 0) | |
195 | result = strcmp (*s1, *s2); | |
196 | ||
197 | return (result); | |
198 | #endif /* !HAVE_STRCOLL */ | |
199 | } | |
200 | ||
201 | /* Sort ARRAY, a null terminated array of pointers to strings. */ | |
202 | void | |
203 | sort_char_array (array) | |
204 | char **array; | |
205 | { | |
206 | qsort (array, array_len (array), sizeof (char *), | |
207 | (Function *)qsort_string_compare); | |
208 | } | |
209 | ||
210 | /* Cons up a new array of words. The words are taken from LIST, | |
211 | which is a WORD_LIST *. If COPY is true, everything is malloc'ed, | |
212 | so you should free everything in this array when you are done. | |
213 | The array is NULL terminated. If IP is non-null, it gets the | |
214 | number of words in the returned array. STARTING_INDEX says where | |
215 | to start filling in the returned array; it can be used to reserve | |
216 | space at the beginning of the array. */ | |
217 | char ** | |
218 | word_list_to_argv (list, copy, starting_index, ip) | |
219 | WORD_LIST *list; | |
220 | int copy, starting_index, *ip; | |
221 | { | |
222 | int count; | |
223 | char **array; | |
224 | ||
225 | count = list_length (list); | |
226 | array = (char **)xmalloc ((1 + count + starting_index) * sizeof (char *)); | |
227 | ||
228 | for (count = 0; count < starting_index; count++) | |
229 | array[count] = (char *)NULL; | |
230 | for (count = starting_index; list; count++, list = list->next) | |
231 | array[count] = copy ? savestring (list->word->word) : list->word->word; | |
232 | array[count] = (char *)NULL; | |
233 | ||
234 | if (ip) | |
235 | *ip = count; | |
236 | return (array); | |
237 | } | |
238 | ||
239 | /* Convert an array of strings into the form used internally by the shell. | |
240 | COPY means to copy the values in ARRAY into the returned list rather | |
241 | than allocate new storage. STARTING_INDEX says where in ARRAY to begin. */ | |
242 | WORD_LIST * | |
243 | argv_to_word_list (array, copy, starting_index) | |
244 | char **array; | |
245 | int copy, starting_index; | |
246 | { | |
247 | WORD_LIST *list; | |
248 | WORD_DESC *w; | |
249 | int i, count; | |
250 | ||
251 | if (array == 0 || array[0] == 0) | |
252 | return (WORD_LIST *)NULL; | |
253 | ||
254 | for (count = 0; array[count]; count++) | |
255 | ; | |
256 | ||
257 | for (i = starting_index, list = (WORD_LIST *)NULL; i < count; i++) | |
258 | { | |
259 | w = make_bare_word (copy ? "" : array[i]); | |
260 | if (copy) | |
261 | { | |
262 | free (w->word); | |
263 | w->word = array[i]; | |
264 | } | |
265 | list = make_word_list (w, list); | |
266 | } | |
267 | return (REVERSE_LIST(list, WORD_LIST *)); | |
268 | } | |
269 | ||
270 | /* **************************************************************** */ | |
271 | /* */ | |
272 | /* String Management Functions */ | |
273 | /* */ | |
274 | /* **************************************************************** */ | |
275 | ||
276 | /* Replace occurrences of PAT with REP in STRING. If GLOBAL is non-zero, | |
277 | replace all occurrences, otherwise replace only the first. | |
278 | This returns a new string; the caller should free it. */ | |
279 | char * | |
280 | strsub (string, pat, rep, global) | |
281 | char *string, *pat, *rep; | |
282 | int global; | |
283 | { | |
284 | int patlen, templen, tempsize, repl, i; | |
285 | char *temp, *r; | |
286 | ||
287 | patlen = strlen (pat); | |
288 | for (temp = (char *)NULL, i = templen = tempsize = 0, repl = 1; string[i]; ) | |
289 | { | |
290 | if (repl && STREQN (string + i, pat, patlen)) | |
291 | { | |
292 | RESIZE_MALLOCED_BUFFER (temp, templen, patlen, tempsize, (patlen * 2)); | |
293 | ||
294 | for (r = rep; *r; ) | |
295 | temp[templen++] = *r++; | |
296 | ||
297 | i += patlen; | |
298 | repl = global != 0; | |
299 | } | |
300 | else | |
301 | { | |
302 | RESIZE_MALLOCED_BUFFER (temp, templen, 1, tempsize, 16); | |
303 | temp[templen++] = string[i++]; | |
304 | } | |
305 | } | |
306 | temp[templen] = 0; | |
307 | return (temp); | |
308 | } | |
309 | ||
310 | /* Remove all leading whitespace from STRING. This includes | |
311 | newlines. STRING should be terminated with a zero. */ | |
312 | void | |
313 | strip_leading (string) | |
314 | char *string; | |
315 | { | |
316 | char *start = string; | |
317 | ||
318 | while (*string && (whitespace (*string) || *string == '\n')) | |
319 | string++; | |
320 | ||
321 | if (string != start) | |
322 | { | |
323 | int len = strlen (string); | |
324 | FASTCOPY (string, start, len); | |
325 | start[len] = '\0'; | |
326 | } | |
327 | } | |
328 | ||
329 | /* Remove all trailing whitespace from STRING. This includes | |
330 | newlines. If NEWLINES_ONLY is non-zero, only trailing newlines | |
331 | are removed. STRING should be terminated with a zero. */ | |
332 | void | |
333 | strip_trailing (string, newlines_only) | |
334 | char *string; | |
335 | int newlines_only; | |
336 | { | |
337 | int len = strlen (string) - 1; | |
338 | ||
339 | while (len >= 0) | |
340 | { | |
341 | if ((newlines_only && string[len] == '\n') || | |
342 | (!newlines_only && whitespace (string[len]))) | |
343 | len--; | |
344 | else | |
345 | break; | |
346 | } | |
347 | string[len + 1] = '\0'; | |
348 | } | |
349 | ||
350 | /* Determine if s2 occurs in s1. If so, return a pointer to the | |
351 | match in s1. The compare is case insensitive. This is a | |
352 | case-insensitive strstr(3). */ | |
353 | char * | |
354 | strindex (s1, s2) | |
355 | char *s1, *s2; | |
356 | { | |
357 | register int i, l, len, c; | |
358 | ||
359 | c = to_upper (s2[0]); | |
360 | for (i = 0, len = strlen (s1), l = strlen (s2); (len - i) >= l; i++) | |
361 | if ((to_upper (s1[i]) == c) && (strncasecmp (s1 + i, s2, l) == 0)) | |
362 | return (s1 + i); | |
363 | return ((char *)NULL); | |
364 | } | |
365 | ||
366 | /* A wrapper for bcopy that can be prototyped in general.h */ | |
367 | void | |
368 | xbcopy (s, d, n) | |
369 | char *s, *d; | |
370 | int n; | |
371 | { | |
372 | FASTCOPY (s, d, n); | |
373 | } |