1/* stringlib.c - Miscellaneous string functions. */
2
3/* Copyright (C) 1996-2002 Free Software Foundation, Inc.
4
5   This file is part of GNU Bash, the Bourne Again SHell.
6
7   Bash is free software; you can redistribute it and/or modify it under
8   the terms of the GNU General Public License as published by the Free
9   Software Foundation; either version 2, or (at your option) any later
10   version.
11
12   Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or
14   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15   for more details.
16
17   You should have received a copy of the GNU General Public License along
18   with Bash; see the file COPYING.  If not, write to the Free Software
19   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20
21#include "config.h"
22
23#include "bashtypes.h"
24
25#if defined (HAVE_UNISTD_H)
26#  include <unistd.h>
27#endif
28
29#include "bashansi.h"
30#include <stdio.h>
31#include "chartypes.h"
32
33#include "shell.h"
34#include "pathexp.h"
35
36#include <glob/glob.h>
37
38#if defined (EXTENDED_GLOB)
39#  include <glob/strmatch.h>
40#endif
41
42/* **************************************************************** */
43/*								    */
44/*		Functions to manage arrays of strings		    */
45/*								    */
46/* **************************************************************** */
47
48/* Find STRING in ALIST, a list of string key/int value pairs.  If FLAGS
49   is 1, STRING is treated as a pattern and matched using strmatch. */
50int
51find_string_in_alist (string, alist, flags)
52     char *string;
53     STRING_INT_ALIST *alist;
54     int flags;
55{
56  register int i;
57  int r;
58
59  for (i = r = 0; alist[i].word; i++)
60    {
61#if defined (EXTENDED_GLOB)
62      if (flags)
63	r = strmatch (alist[i].word, string, FNM_EXTMATCH) != FNM_NOMATCH;
64      else
65#endif
66	r = STREQ (string, alist[i].word);
67
68      if (r)
69	return (alist[i].token);
70    }
71  return -1;
72}
73
74/* Find TOKEN in ALIST, a list of string/int value pairs.  Return the
75   corresponding string.  Allocates memory for the returned
76   string.  FLAGS is currently ignored, but reserved. */
77char *
78find_token_in_alist (token, alist, flags)
79     int token;
80     STRING_INT_ALIST *alist;
81     int flags;
82{
83  register int i;
84
85  for (i = 0; alist[i].word; i++)
86    {
87      if (alist[i].token == token)
88        return (savestring (alist[i].word));
89    }
90  return ((char *)NULL);
91}
92
93int
94find_index_in_alist (string, alist, flags)
95     char *string;
96     STRING_INT_ALIST *alist;
97     int flags;
98{
99  register int i;
100  int r;
101
102  for (i = r = 0; alist[i].word; i++)
103    {
104#if defined (EXTENDED_GLOB)
105      if (flags)
106	r = strmatch (alist[i].word, string, FNM_EXTMATCH) != FNM_NOMATCH;
107      else
108#endif
109	r = STREQ (string, alist[i].word);
110
111      if (r)
112	return (i);
113    }
114
115  return -1;
116}
117
118/* **************************************************************** */
119/*								    */
120/*		    String Management Functions			    */
121/*								    */
122/* **************************************************************** */
123
124/* Cons a new string from STRING starting at START and ending at END,
125   not including END. */
126char *
127substring (string, start, end)
128     char *string;
129     int start, end;
130{
131  register int len;
132  register char *result;
133
134  len = end - start;
135  result = (char *)xmalloc (len + 1);
136  strncpy (result, string + start, len);
137  result[len] = '\0';
138  return (result);
139}
140
141/* Replace occurrences of PAT with REP in STRING.  If GLOBAL is non-zero,
142   replace all occurrences, otherwise replace only the first.
143   This returns a new string; the caller should free it. */
144char *
145strsub (string, pat, rep, global)
146     char *string, *pat, *rep;
147     int global;
148{
149  int patlen, replen, templen, tempsize, repl, i;
150  char *temp, *r;
151
152  patlen = strlen (pat);
153  replen = strlen (rep);
154  for (temp = (char *)NULL, i = templen = tempsize = 0, repl = 1; string[i]; )
155    {
156      if (repl && STREQN (string + i, pat, patlen))
157	{
158	  if (replen)
159	    RESIZE_MALLOCED_BUFFER (temp, templen, replen, tempsize, (replen * 2));
160
161	  for (r = rep; *r; )
162	    temp[templen++] = *r++;
163
164	  i += patlen ? patlen : 1;	/* avoid infinite recursion */
165	  repl = global != 0;
166	}
167      else
168	{
169	  RESIZE_MALLOCED_BUFFER (temp, templen, 1, tempsize, 16);
170	  temp[templen++] = string[i++];
171	}
172    }
173  temp[templen] = 0;
174  return (temp);
175}
176
177/* Replace all instances of C in STRING with TEXT.  TEXT may be empty or
178   NULL.  If DO_GLOB is non-zero, we quote the replacement text for
179   globbing.  Backslash may be used to quote C. */
180char *
181strcreplace (string, c, text, do_glob)
182     char *string;
183     int c;
184     char *text;
185     int do_glob;
186{
187  char *ret, *p, *r, *t;
188  int len, rlen, ind, tlen;
189
190  len = STRLEN (text);
191  rlen = len + strlen (string) + 2;
192  ret = (char *)xmalloc (rlen);
193
194  for (p = string, r = ret; p && *p; )
195    {
196      if (*p == c)
197	{
198	  if (len)
199	    {
200	      ind = r - ret;
201	      if (do_glob && (glob_pattern_p (text) || strchr (text, '\\')))
202		{
203		  t = quote_globbing_chars (text);
204		  tlen = strlen (t);
205		  RESIZE_MALLOCED_BUFFER (ret, ind, tlen, rlen, rlen);
206		  r = ret + ind;	/* in case reallocated */
207		  strcpy (r, t);
208		  r += tlen;
209		  free (t);
210		}
211	      else
212		{
213		  RESIZE_MALLOCED_BUFFER (ret, ind, len, rlen, rlen);
214		  r = ret + ind;	/* in case reallocated */
215		  strcpy (r, text);
216		  r += len;
217		}
218	    }
219	  p++;
220	  continue;
221	}
222
223      if (*p == '\\' && p[1] == c)
224	p++;
225
226      ind = r - ret;
227      RESIZE_MALLOCED_BUFFER (ret, ind, 2, rlen, rlen);
228      r = ret + ind;			/* in case reallocated */
229      *r++ = *p++;
230    }
231  *r = '\0';
232
233  return ret;
234}
235
236#ifdef INCLUDE_UNUSED
237/* Remove all leading whitespace from STRING.  This includes
238   newlines.  STRING should be terminated with a zero. */
239void
240strip_leading (string)
241     char *string;
242{
243  char *start = string;
244
245  while (*string && (whitespace (*string) || *string == '\n'))
246    string++;
247
248  if (string != start)
249    {
250      int len = strlen (string);
251      FASTCOPY (string, start, len);
252      start[len] = '\0';
253    }
254}
255#endif
256
257/* Remove all trailing whitespace from STRING.  This includes
258   newlines.  If NEWLINES_ONLY is non-zero, only trailing newlines
259   are removed.  STRING should be terminated with a zero. */
260void
261strip_trailing (string, len, newlines_only)
262     char *string;
263     int len;
264     int newlines_only;
265{
266  while (len >= 0)
267    {
268      if ((newlines_only && string[len] == '\n') ||
269	  (!newlines_only && whitespace (string[len])))
270	len--;
271      else
272	break;
273    }
274  string[len + 1] = '\0';
275}
276
277/* A wrapper for bcopy that can be prototyped in general.h */
278void
279xbcopy (s, d, n)
280     char *s, *d;
281     int n;
282{
283  FASTCOPY (s, d, n);
284}
285