Add to git.
[c2lib.git] / pstring.c
1 /* String functions which allocate strings on the pool.
2  * By Richard W.M. Jones <rich@annexia.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: pstring.c,v 1.24 2003/01/22 14:29:37 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdarg.h>
25
26 #ifdef HAVE_ALLOCA_H
27 #include <alloca.h>
28 #endif
29
30 #ifdef HAVE_CTYPE_H
31 #include <ctype.h>
32 #endif
33
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37
38 #include <vector.h>
39 #include <hash.h>
40 #include <pstring.h>
41
42 #define MIN(a,b) ((a)<(b)?(a):(b))
43
44 /* Duplicate a string. */
45 char *
46 pstrdup (pool pool, const char *str)
47 {
48   int len = strlen (str);
49   char *ptr = pmalloc (pool, (len+1) * sizeof (char));
50   return memcpy (ptr, str, len+1);
51 }
52
53 /* Duplicate up to the first N characters of a string. */
54 char *
55 pstrndup (pool pool, const char *str, int n)
56 {
57   int len = MIN (strlen (str), n);
58   char *ptr = pmalloc (pool, (len+1) * sizeof (char));
59   memcpy (ptr, str, len);
60   ptr[len] = '\0';
61   return ptr;
62 }
63
64 /* Duplicate a fixed-size area of memory. */
65 void *
66 pmemdup (pool pool, const void *data, size_t size)
67 {
68   void *ptr = pmalloc (pool, size);
69   return memcpy (ptr, data, size);
70 }
71
72 static vector generic_split (pool pool, const char *str, const void *sep, const char *(*find) (const char *str, const void *sep, const char **end_match), int keep);
73
74 static const char *
75 find_strstr (const char *str, const void *sep, const char **end_match)
76 {
77   const char *csep = (const char *) sep;
78   const char *t = strstr (str, csep);
79   if (t) *end_match = t + strlen (csep);
80   return t;
81 }
82
83 vector
84 pstrsplit (pool pool, const char *str, const char *sep)
85 {
86   return generic_split (pool, str, sep, find_strstr, 0);
87 }
88
89 static const char *
90 find_strchr (const char *str, const void *sep, const char **end_match)
91 {
92   char c = * (const char *) sep;
93   const char *t = strchr (str, c);
94   if (t) *end_match = t+1;
95   return t;
96 }
97
98 vector
99 pstrcsplit (pool pool, const char *str, char c)
100 {
101   return generic_split (pool, str, &c, find_strchr, 0);
102 }
103
104 static const char *
105 find_re (const char *str, const void *sep, const char **end_match)
106 {
107   const pcre *re = (const pcre *) sep;
108 #define ovecsize 3
109   int ovector[ovecsize];
110   int r = pcre_exec (re, 0, str, strlen (str), 0, 0, ovector, ovecsize);
111
112   if (r >= 0)                   /* Successful match. */
113     {
114       int so = ovector[0];
115       int eo = ovector[1];
116
117       if (so == -1) abort ();   /* Bad pattern. */
118       *end_match = str + eo;
119       return str + so;
120     }
121   else if (r == PCRE_ERROR_NOMATCH)
122     return 0;
123   else
124     abort ();                   /* Some other error reported by PCRE. */
125 #undef ovecsize
126 }
127
128 vector
129 pstrresplit (pool pool, const char *str, const pcre *re)
130 {
131   return generic_split (pool, str, re, find_re, 0);
132 }
133
134 vector
135 pstrsplit2 (pool pool, const char *str, const char *sep)
136 {
137   return generic_split (pool, str, sep, find_strstr, 1);
138 }
139
140 vector
141 pstrcsplit2 (pool pool, const char *str, char c)
142 {
143   return generic_split (pool, str, &c, find_strchr, 1);
144 }
145
146 vector
147 pstrresplit2 (pool pool, const char *str, const pcre *re)
148 {
149   return generic_split (pool, str, re, find_re, 1);
150 }
151
152 /* Generic split function. */
153 static vector
154 generic_split (pool pool, const char *str, const void *sep,
155                const char *(*find) (const char *str, const void *sep,
156                                     const char **end_match),
157                int keep)
158 {
159   const char *start_match, *end_match;
160   char *s;
161   vector v;
162
163   /* If the string is zero length, always return a zero length vector. */
164   if (strcmp (str, "") == 0) return new_vector (pool, char *);
165
166   /* Find the splitting point. */
167   start_match = find (str, sep, &end_match);
168
169   if (start_match != 0)         /* Successful match. */
170     {
171       s = start_match > str ? pstrndup (pool, str, start_match - str) : 0;
172       v = generic_split (pool, end_match, sep, find, keep);
173       if (keep)                 /* Keep the matching text. */
174         {
175           const char *match;
176
177           match = pstrndup (pool, start_match, end_match - start_match);
178           vector_push_front (v, match);
179         }
180       if (s) vector_push_front (v, s);
181     }
182   else                          /* Not successful match. */
183     {
184       s = pstrdup (pool, str);
185       v = new_vector (pool, char *);
186       vector_push_back (v, s);
187     }
188
189   return v;
190 }
191
192 /* Concatenate a vector of strings to form a string. */
193 char *
194 pconcat (pool pool, vector v)
195 {
196   int i;
197   char *s = pstrdup (pool, "");
198
199   for (i = 0; i < vector_size (v); ++i)
200     {
201       char *t;
202
203       vector_get (v, i, t);
204       s = pstrcat (pool, s, t);
205     }
206
207   return s;
208 }
209
210 /* Join a vector of strings, separating each string by the given string. */
211 char *
212 pjoin (pool pool, vector v, const char *sep)
213 {
214   int i;
215   char *s = pstrdup (pool, "");
216
217   for (i = 0; i < vector_size (v); ++i)
218     {
219       char *t;
220
221       vector_get (v, i, t);
222       s = pstrcat (pool, s, t);
223       if (i < vector_size (v) - 1) s = pstrcat (pool, s, sep);
224     }
225
226   return s;
227 }
228
229 char *
230 pchrs (pool pool, char c, int n)
231 {
232   char *s = pmalloc (pool, sizeof (char) * (n + 1));
233   int i;
234
235   for (i = 0; i < n; ++i)
236     s[i] = c;
237   s[n] = '\0';
238
239   return s;
240 }
241
242 char *
243 pstrs (pool pool, const char *str, int n)
244 {
245   int len = strlen (str);
246   char *s = pmalloc (pool, sizeof (char) * (len * n + 1));
247   int i, j;
248
249   for (i = j = 0; i < n; ++i, j += len)
250     memcpy (&s[j], str, len);
251
252   s[len * n] = '\0';
253
254   return s;
255 }
256
257 vector
258 pvector (pool pool, ...)
259 {
260   va_list args;
261   const char *s;
262   vector v = new_vector (pool, const char *);
263
264   va_start (args, pool);
265   while ((s = va_arg (args, const char *)) != 0)
266     vector_push_back (v, s);
267   va_end (args);
268
269   return v;
270 }
271
272 vector
273 pvectora (pool pool, const char *array[], int n)
274 {
275   int i;
276   vector v = new_vector (pool, const char *);
277
278   for (i = 0; i < n; ++i)
279     vector_push_back (v, array[i]);
280
281   return v;
282 }
283
284 /* Sort a vector of strings. */
285 void
286 psort (vector v, int (*compare_fn) (const char **, const char **))
287 {
288   vector_sort (v, (int (*) (const void *, const void *)) compare_fn);
289 }
290
291 /* Remove line endings (either CR, CRLF or LF) from the string. */
292 char *
293 pchomp (char *line)
294 {
295   int len = strlen (line);
296
297   while (line[len-1] == '\n' || line[len-1] == '\r')
298     line[--len] = '\0';
299
300   return line;
301 }
302
303 char *
304 ptrimfront (char *str)
305 {
306   char *p;
307   int len;
308
309   for (p = str; *p && isspace ((int) *p); ++p)
310     ;
311
312   len = strlen (p);
313   memmove (str, p, len + 1);
314
315   return str;
316 }
317
318 char *
319 ptrimback (char *str)
320 {
321   int len;
322   char *p;
323
324   len = strlen (str);
325   for (p = str + len - 1; p >= str && isspace ((int) *p); --p)
326     ;
327
328   p[1] = '\0';
329
330   return str;
331 }
332
333 char *
334 ptrim (char *str)
335 {
336   ptrimback (str);
337   ptrimfront (str);
338   return str;
339 }
340
341 /* This is equivalent to sprintf but it allocates the result string in POOL.*/
342 char *
343 psprintf (pool pool, const char *format, ...)
344 {
345   va_list args;
346   char *s;
347
348   va_start (args, format);
349   s = pvsprintf (pool, format, args);
350   va_end (args);
351
352   return s;
353 }
354
355 /* Similar to vsprintf. */
356 char *
357 pvsprintf (pool pool, const char *format, va_list args)
358 {
359 #ifdef HAVE_VASPRINTF
360
361   char *s;
362
363   vasprintf (&s, format, args);
364   if (s == 0) abort ();         /* XXX Should call bad_malloc_handler. */
365
366   /* The pool will clean up the malloc when it goes. */
367   pool_register_malloc (pool, s);
368
369   return s;
370
371 #else /* !HAVE_VASPRINTF */
372
373   int r, n = 256;
374   char *s = alloca (n), *t;
375
376   /* Note: according to the manual page, a return value of -1 indicates
377    * that the string was truncated. We have found that this is not
378    * actually true however. In fact, the library seems to return the
379    * number of characters which would have been written into the string
380    * excluding the '\0' (ie. r > n).
381    */
382   r = vsnprintf (s, n, format, args);
383
384   if (r < n)
385     {
386       /* Copy the string into a pool-allocated area of the correct size
387        * and return it.
388        */
389       n = r + 1;
390       t = pmalloc (pool, n);
391       memcpy (t, s, n);
392
393       return t;
394     }
395   else
396     {
397       /* String was truncated. Allocate enough space for the string
398        * in the pool and repeat the vsnprintf into this buffer.
399        */
400       n = r + 1;
401       t = pmalloc (pool, n);
402
403       vsnprintf (t, n, format, args);
404
405       return t;
406     }
407
408 #endif /* !HAVE_VASPRINTF */
409 }
410
411 /* Convert various number types to strings. */
412 char *
413 pitoa (pool pool, int n)
414 {
415   char *s = pmalloc (pool, 16);
416   snprintf (s, 16, "%d", n);
417   return s;
418 }
419
420 char *
421 pdtoa (pool pool, double n)
422 {
423   char *s = pmalloc (pool, 16);
424   snprintf (s, 16, "%f", n);
425   return s;
426 }
427
428 char *
429 pxtoa (pool pool, unsigned n)
430 {
431   char *s = pmalloc (pool, 16);
432   snprintf (s, 16, "%x", n);
433   return s;
434 }
435
436 /* Promote vector of numbers to vector of strings. */
437 vector
438 pvitostr (pool pool, vector v)
439 {
440   vector nv = new_vector (pool, char *);
441   int i;
442
443   vector_reallocate (nv, vector_size (v));
444
445   for (i = 0; i < vector_size (v); ++i)
446     {
447       char *s;
448       int j;
449
450       vector_get (v, i, j);
451       s = pitoa (pool, j);
452       vector_push_back (nv, s);
453     }
454
455   return nv;
456 }
457
458 vector
459 pvdtostr (pool pool, vector v)
460 {
461   vector nv = new_vector (pool, char *);
462   int i;
463
464   vector_reallocate (nv, vector_size (v));
465
466   for (i = 0; i < vector_size (v); ++i)
467     {
468       char *s;
469       double j;
470
471       vector_get (v, i, j);
472       s = pdtoa (pool, j);
473       vector_push_back (nv, s);
474     }
475
476   return nv;
477 }
478
479 vector
480 pvxtostr (pool pool, vector v)
481 {
482   vector nv = new_vector (pool, char *);
483   int i;
484
485   vector_reallocate (nv, vector_size (v));
486
487   for (i = 0; i < vector_size (v); ++i)
488     {
489       char *s;
490       unsigned j;
491
492       vector_get (v, i, j);
493       s = pxtoa (pool, j);
494       vector_push_back (nv, s);
495     }
496
497   return nv;
498 }
499
500 /* STR is a string allocated in POOL. Append ENDING to STR, reallocating
501  * STR if necessary.
502  */
503 char *
504 pstrcat (pool pool, char *str, const char *ending)
505 {
506   /* There are probably more efficient ways to implement this ... */
507   int slen = strlen (str);
508   int elen = strlen (ending);
509
510   str = prealloc (pool, str, slen + elen + 1);
511   strcat (str, ending);
512   return str;
513 }
514
515 char *
516 pstrncat (pool pool, char *str, const char *ending, size_t n)
517 {
518   int slen = strlen (str);
519   int elen = strlen (ending);
520
521   elen = elen > n ? n : elen;
522
523   str = prealloc (pool, str, slen + elen + 1);
524   strncat (str, ending, n);
525   return str;
526 }
527
528 /* Return the substring starting at OFFSET and of length LEN of STR, allocated
529  * as a new string. If LEN is negative, everything up to the end of STR
530  * is returned.
531  */
532 char *
533 psubstr (pool pool, const char *str, int offset, int len)
534 {
535   char *new_str;
536
537   if (len >= 0)
538     {
539       new_str = pmalloc (pool, len + 1);
540       memcpy (new_str, str + offset, len);
541       new_str[len] = '\0';
542       return new_str;
543     }
544   else
545     {
546       len = strlen (str + offset);
547       new_str = pmalloc (pool, len + 1);
548       memcpy (new_str, str + offset, len);
549       new_str[len] = '\0';
550       return new_str;
551     }
552 }
553
554 char *
555 pstrupr (char *str)
556 {
557   char *s = str;
558   while (*s) { *s = toupper (*s); s++; }
559   return str;
560 }
561
562 char *
563 pstrlwr (char *str)
564 {
565   char *s = str;
566   while (*s) { *s = tolower (*s); s++; }
567   return str;
568 }
569
570 /* NB. The following figures were derived by examining a large number
571  * of configuration files in /etc/ on a Red Hat Linux box.
572  */
573 #define _PGETL_INITIAL_BUFFER 96
574 #define _PGETL_INCR_BUFFER 32
575
576 char *
577 pgetline (pool pool, FILE *fp, char *line)
578 {
579   int allocated = _PGETL_INITIAL_BUFFER;
580   int len = 0;
581   int c;
582
583   /* Reallocate the buffer. */
584   line = prealloc (pool, line, _PGETL_INITIAL_BUFFER);
585
586   /* Read in the line until we reach EOF or a '\n' character. */
587   while ((c = getc (fp)) != EOF && c != '\n')
588     {
589       if (len == allocated)
590         line = prealloc (pool, line, allocated += _PGETL_INCR_BUFFER);
591       line[len++] = c;
592     }
593
594   /* EOF and no content? */
595   if (c == EOF && len == 0)
596     return 0;
597
598   /* Last character is '\r'? Remove it. */
599   if (line[len-1] == '\r')
600     len--;
601
602   /* Append a '\0' character to the buffer. */
603   if (len == allocated)
604     line = prealloc (pool, line, ++allocated);
605   line[len] = '\0';
606
607   return line;
608 }
609
610 char *
611 pgetlinex (pool pool, FILE *fp, char *line, const char *comment_set,
612            int flags)
613 {
614   int i, len;
615
616  again:
617   /* Read a single line. */
618   line = pgetline (pool, fp, line);
619   if (line == 0) return 0;
620
621   len = strlen (line);
622
623   /* Concatenate? */
624   if (!(flags & PGETL_NO_CONCAT))
625     {
626     another_concat:
627       if (line[len-1] == '\\')
628         {
629           char *next;
630
631           line[--len] = '\0';   /* Remove backslash char from first line. */
632
633           next = pgetline (pool, fp, 0);
634           if (next)
635             {
636               line = pstrcat (pool, line, next);
637               len = strlen (line);
638               goto another_concat;
639             }
640         }
641     }
642
643   /* Remove comments? */
644   if (!(flags & PGETL_INLINE_COMMENTS))
645     {
646       /* No inline comments. We're searching for whitespace followed
647        * by a comment character. If we find it, remove the line following
648        * the comment character.
649        */
650       for (i = 0; i < len; ++i)
651         if (!isspace ((int) line[i]))
652           {
653             if (strchr (comment_set, line[i]) != 0)
654               {
655                 line[i] = '\0';
656                 len = i;
657               }
658             break;
659           }
660     }
661   else
662     {
663       /* Inline comments. Search for the first occurance of any
664        * comment character and just remove the rest of the line
665        * from that point.
666        */
667       for (i = 0; i < len; ++i)
668         if (strchr (comment_set, line[i]) != 0)
669           {
670             line[i] = '\0';
671             len = i;
672             break;
673           }
674     }
675
676   /* Trim the line. */
677   ptrim (line);
678
679   /* Ignore blank lines. */
680   if (line[0] == '\0')
681     goto again;
682
683   return line;
684 }
685
686 vector
687 pmap (pool p, const vector v, char *(*map_fn) (pool, const char *))
688 {
689   int i;
690   vector nv = new_vector (p, char *);
691
692   for (i = 0; i < vector_size (v); ++i)
693     {
694       const char *s;
695       char *r;
696
697       vector_get (v, i, s);
698       r = map_fn (p, s);
699       vector_push_back (nv, r);
700     }
701
702   return nv;
703 }
704
705 vector
706 pgrep (pool p, const vector v, int (*grep_fn) (pool, const char *))
707 {
708   int i;
709   vector nv = new_vector (p, char *);
710
711   for (i = 0; i < vector_size (v); ++i)
712     {
713       const char *s;
714
715       vector_get (v, i, s);
716       if (grep_fn (p, s))
717         vector_push_back (nv, s);
718     }
719
720   return nv;
721 }