]>
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 | ||
cce855bc JA |
41 | #define ISOCTAL(c) ((c) >= '0' && (c) <= '7') |
42 | #define OCTVALUE(c) ((c) - '0') | |
43 | ||
44 | #ifndef isxdigit | |
45 | # define isxdigit(c) (isdigit((c)) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) | |
46 | #endif | |
47 | ||
48 | #define HEXVALUE(c) \ | |
49 | ((c) >= 'a' && (c) <= 'f' ? (c)-'a'+10 : (c) >= 'A' && (c) <= 'F' ? (c)-'A'+10 : (c)-'0') | |
50 | ||
ccc6cda3 JA |
51 | /* Convert STRING by expanding the escape sequences specified by the |
52 | ANSI C standard. If SAWC is non-null, recognize `\c' and use that | |
53 | as a string terminator. If we see \c, set *SAWC to 1 before | |
54 | returning. LEN is the length of STRING. */ | |
55 | char * | |
d166f048 | 56 | ansicstr (string, len, sawc, rlen) |
ccc6cda3 | 57 | char *string; |
d166f048 | 58 | int len, *sawc, *rlen; |
ccc6cda3 | 59 | { |
cce855bc | 60 | int c, temp; |
ccc6cda3 JA |
61 | char *ret, *r, *s; |
62 | ||
63 | if (string == 0 || *string == '\0') | |
64 | return ((char *)NULL); | |
65 | ||
66 | ret = xmalloc (len + 1); | |
67 | for (r = ret, s = string; s && *s; ) | |
68 | { | |
69 | c = *s++; | |
70 | if (c != '\\' || *s == '\0') | |
71 | *r++ = c; | |
72 | else | |
73 | { | |
74 | switch (c = *s++) | |
75 | { | |
76 | #if defined (__STDC__) | |
77 | case 'a': c = '\a'; break; | |
78 | case 'v': c = '\v'; break; | |
79 | #else | |
80 | case 'a': c = '\007'; break; | |
81 | case 'v': c = (int) 0x0B; break; | |
82 | #endif | |
83 | case 'b': c = '\b'; break; | |
cce855bc JA |
84 | case 'e': case 'E': /* ESC -- non-ANSI */ |
85 | c = '\033'; break; | |
ccc6cda3 JA |
86 | case 'f': c = '\f'; break; |
87 | case 'n': c = '\n'; break; | |
88 | case 'r': c = '\r'; break; | |
89 | case 't': c = '\t'; break; | |
90 | case '0': case '1': case '2': case '3': | |
91 | case '4': case '5': case '6': case '7': | |
cce855bc JA |
92 | for (temp = 2, c -= '0'; ISOCTAL (*s) && temp--; s++) |
93 | c = (c * 8) + OCTVALUE (*s); | |
94 | break; | |
95 | case 'x': /* Hex digit -- non-ANSI */ | |
96 | for (temp = 3, c = 0; isxdigit (*s) && temp--; s++) | |
97 | c = (c * 16) + HEXVALUE (*s); | |
98 | /* \x followed by non-hex digits is passed through unchanged */ | |
99 | if (temp == 3) | |
100 | { | |
101 | *r++ = '\\'; | |
102 | c = 'x'; | |
103 | } | |
ccc6cda3 JA |
104 | break; |
105 | case '\\': | |
106 | case '\'': | |
107 | break; | |
108 | case 'c': | |
109 | if (sawc) | |
110 | { | |
111 | *sawc = 1; | |
112 | *r = '\0'; | |
d166f048 JA |
113 | if (rlen) |
114 | *rlen = r - ret; | |
ccc6cda3 JA |
115 | return ret; |
116 | } | |
117 | default: *r++ = '\\'; break; | |
118 | } | |
119 | *r++ = c; | |
120 | } | |
121 | } | |
122 | *r = '\0'; | |
d166f048 JA |
123 | if (rlen) |
124 | *rlen = r - ret; | |
ccc6cda3 JA |
125 | return ret; |
126 | } | |
127 | ||
128 | /* **************************************************************** */ | |
129 | /* */ | |
130 | /* Functions to manage arrays of strings */ | |
131 | /* */ | |
132 | /* **************************************************************** */ | |
133 | ||
cce855bc | 134 | #ifdef INCLUDE_UNUSED |
ccc6cda3 JA |
135 | /* Find NAME in ARRAY. Return the index of NAME, or -1 if not present. |
136 | ARRAY should be NULL terminated. */ | |
137 | int | |
138 | find_name_in_array (name, array) | |
139 | char *name, **array; | |
140 | { | |
141 | int i; | |
142 | ||
143 | for (i = 0; array[i]; i++) | |
144 | if (STREQ (name, array[i])) | |
145 | return (i); | |
146 | ||
147 | return (-1); | |
148 | } | |
cce855bc | 149 | #endif |
ccc6cda3 JA |
150 | |
151 | /* Return the length of ARRAY, a NULL terminated array of char *. */ | |
152 | int | |
153 | array_len (array) | |
154 | char **array; | |
155 | { | |
156 | register int i; | |
157 | ||
158 | for (i = 0; array[i]; i++); | |
159 | return (i); | |
160 | } | |
161 | ||
162 | /* Free the contents of ARRAY, a NULL terminated array of char *. */ | |
163 | void | |
164 | free_array_members (array) | |
165 | char **array; | |
166 | { | |
167 | register int i; | |
168 | ||
169 | if (array == 0) | |
170 | return; | |
171 | ||
172 | for (i = 0; array[i]; i++) | |
173 | free (array[i]); | |
174 | } | |
175 | ||
176 | void | |
177 | free_array (array) | |
178 | char **array; | |
179 | { | |
180 | if (array == 0) | |
181 | return; | |
182 | ||
183 | free_array_members (array); | |
184 | free (array); | |
185 | } | |
186 | ||
187 | /* Allocate and return a new copy of ARRAY and its contents. */ | |
188 | char ** | |
189 | copy_array (array) | |
190 | char **array; | |
191 | { | |
192 | register int i; | |
193 | int len; | |
194 | char **new_array; | |
195 | ||
196 | len = array_len (array); | |
197 | ||
198 | new_array = (char **)xmalloc ((len + 1) * sizeof (char *)); | |
199 | for (i = 0; array[i]; i++) | |
200 | new_array[i] = savestring (array[i]); | |
201 | new_array[i] = (char *)NULL; | |
202 | ||
203 | return (new_array); | |
204 | } | |
205 | ||
206 | /* Comparison routine for use with qsort() on arrays of strings. Uses | |
207 | strcoll(3) if available, otherwise it uses strcmp(3). */ | |
208 | int | |
209 | qsort_string_compare (s1, s2) | |
210 | register char **s1, **s2; | |
211 | { | |
212 | #if defined (HAVE_STRCOLL) | |
213 | return (strcoll (*s1, *s2)); | |
214 | #else /* !HAVE_STRCOLL */ | |
215 | int result; | |
216 | ||
217 | if ((result = **s1 - **s2) == 0) | |
218 | result = strcmp (*s1, *s2); | |
219 | ||
220 | return (result); | |
221 | #endif /* !HAVE_STRCOLL */ | |
222 | } | |
223 | ||
224 | /* Sort ARRAY, a null terminated array of pointers to strings. */ | |
225 | void | |
226 | sort_char_array (array) | |
227 | char **array; | |
228 | { | |
229 | qsort (array, array_len (array), sizeof (char *), | |
230 | (Function *)qsort_string_compare); | |
231 | } | |
232 | ||
233 | /* Cons up a new array of words. The words are taken from LIST, | |
234 | which is a WORD_LIST *. If COPY is true, everything is malloc'ed, | |
235 | so you should free everything in this array when you are done. | |
236 | The array is NULL terminated. If IP is non-null, it gets the | |
237 | number of words in the returned array. STARTING_INDEX says where | |
238 | to start filling in the returned array; it can be used to reserve | |
239 | space at the beginning of the array. */ | |
240 | char ** | |
241 | word_list_to_argv (list, copy, starting_index, ip) | |
242 | WORD_LIST *list; | |
243 | int copy, starting_index, *ip; | |
244 | { | |
245 | int count; | |
246 | char **array; | |
247 | ||
248 | count = list_length (list); | |
249 | array = (char **)xmalloc ((1 + count + starting_index) * sizeof (char *)); | |
250 | ||
251 | for (count = 0; count < starting_index; count++) | |
252 | array[count] = (char *)NULL; | |
253 | for (count = starting_index; list; count++, list = list->next) | |
254 | array[count] = copy ? savestring (list->word->word) : list->word->word; | |
255 | array[count] = (char *)NULL; | |
256 | ||
257 | if (ip) | |
258 | *ip = count; | |
259 | return (array); | |
260 | } | |
261 | ||
262 | /* Convert an array of strings into the form used internally by the shell. | |
263 | COPY means to copy the values in ARRAY into the returned list rather | |
264 | than allocate new storage. STARTING_INDEX says where in ARRAY to begin. */ | |
265 | WORD_LIST * | |
266 | argv_to_word_list (array, copy, starting_index) | |
267 | char **array; | |
268 | int copy, starting_index; | |
269 | { | |
270 | WORD_LIST *list; | |
271 | WORD_DESC *w; | |
272 | int i, count; | |
273 | ||
274 | if (array == 0 || array[0] == 0) | |
275 | return (WORD_LIST *)NULL; | |
276 | ||
277 | for (count = 0; array[count]; count++) | |
278 | ; | |
279 | ||
280 | for (i = starting_index, list = (WORD_LIST *)NULL; i < count; i++) | |
281 | { | |
282 | w = make_bare_word (copy ? "" : array[i]); | |
283 | if (copy) | |
284 | { | |
285 | free (w->word); | |
286 | w->word = array[i]; | |
287 | } | |
288 | list = make_word_list (w, list); | |
289 | } | |
290 | return (REVERSE_LIST(list, WORD_LIST *)); | |
291 | } | |
292 | ||
293 | /* **************************************************************** */ | |
294 | /* */ | |
295 | /* String Management Functions */ | |
296 | /* */ | |
297 | /* **************************************************************** */ | |
298 | ||
299 | /* Replace occurrences of PAT with REP in STRING. If GLOBAL is non-zero, | |
300 | replace all occurrences, otherwise replace only the first. | |
301 | This returns a new string; the caller should free it. */ | |
302 | char * | |
303 | strsub (string, pat, rep, global) | |
304 | char *string, *pat, *rep; | |
305 | int global; | |
306 | { | |
cce855bc | 307 | int patlen, replen, templen, tempsize, repl, i; |
ccc6cda3 JA |
308 | char *temp, *r; |
309 | ||
310 | patlen = strlen (pat); | |
cce855bc | 311 | replen = strlen (rep); |
ccc6cda3 JA |
312 | for (temp = (char *)NULL, i = templen = tempsize = 0, repl = 1; string[i]; ) |
313 | { | |
314 | if (repl && STREQN (string + i, pat, patlen)) | |
315 | { | |
cce855bc | 316 | RESIZE_MALLOCED_BUFFER (temp, templen, replen, tempsize, (replen * 2)); |
ccc6cda3 JA |
317 | |
318 | for (r = rep; *r; ) | |
319 | temp[templen++] = *r++; | |
320 | ||
321 | i += patlen; | |
322 | repl = global != 0; | |
323 | } | |
324 | else | |
325 | { | |
326 | RESIZE_MALLOCED_BUFFER (temp, templen, 1, tempsize, 16); | |
327 | temp[templen++] = string[i++]; | |
328 | } | |
329 | } | |
330 | temp[templen] = 0; | |
331 | return (temp); | |
332 | } | |
333 | ||
d166f048 | 334 | #ifdef INCLUDE_UNUSED |
ccc6cda3 JA |
335 | /* Remove all leading whitespace from STRING. This includes |
336 | newlines. STRING should be terminated with a zero. */ | |
337 | void | |
338 | strip_leading (string) | |
339 | char *string; | |
340 | { | |
341 | char *start = string; | |
342 | ||
343 | while (*string && (whitespace (*string) || *string == '\n')) | |
344 | string++; | |
345 | ||
346 | if (string != start) | |
347 | { | |
348 | int len = strlen (string); | |
349 | FASTCOPY (string, start, len); | |
350 | start[len] = '\0'; | |
351 | } | |
352 | } | |
d166f048 | 353 | #endif |
ccc6cda3 JA |
354 | |
355 | /* Remove all trailing whitespace from STRING. This includes | |
356 | newlines. If NEWLINES_ONLY is non-zero, only trailing newlines | |
357 | are removed. STRING should be terminated with a zero. */ | |
358 | void | |
d166f048 | 359 | strip_trailing (string, len, newlines_only) |
ccc6cda3 | 360 | char *string; |
d166f048 | 361 | int len; |
ccc6cda3 JA |
362 | int newlines_only; |
363 | { | |
ccc6cda3 JA |
364 | while (len >= 0) |
365 | { | |
366 | if ((newlines_only && string[len] == '\n') || | |
367 | (!newlines_only && whitespace (string[len]))) | |
368 | len--; | |
369 | else | |
370 | break; | |
371 | } | |
372 | string[len + 1] = '\0'; | |
373 | } | |
374 | ||
375 | /* Determine if s2 occurs in s1. If so, return a pointer to the | |
376 | match in s1. The compare is case insensitive. This is a | |
377 | case-insensitive strstr(3). */ | |
378 | char * | |
379 | strindex (s1, s2) | |
380 | char *s1, *s2; | |
381 | { | |
382 | register int i, l, len, c; | |
383 | ||
384 | c = to_upper (s2[0]); | |
385 | for (i = 0, len = strlen (s1), l = strlen (s2); (len - i) >= l; i++) | |
386 | if ((to_upper (s1[i]) == c) && (strncasecmp (s1 + i, s2, l) == 0)) | |
387 | return (s1 + i); | |
388 | return ((char *)NULL); | |
389 | } | |
390 | ||
391 | /* A wrapper for bcopy that can be prototyped in general.h */ | |
392 | void | |
393 | xbcopy (s, d, n) | |
394 | char *s, *d; | |
395 | int n; | |
396 | { | |
397 | FASTCOPY (s, d, n); | |
398 | } |