checkin of 0.3.0
[pspp-builds.git] / src / str.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <ctype.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include "alloc.h"
26 #include "error.h"
27 #include "pool.h"
28 #include "str.h"
29 \f
30 /* sprintf() wrapper functions for convenience. */
31
32 #if !__GNUC__
33 char *
34 spprintf (char *buf, const char *format,...)
35 {
36 #if HAVE_GOOD_SPRINTF
37   int count;
38 #endif
39   va_list args;
40
41   va_start (args, format);
42 #if HAVE_GOOD_SPRINTF
43   count =
44 #endif
45     vsprintf (buf, format, args);
46   va_end (args);
47
48 #if HAVE_GOOD_SPRINTF
49   return &buf[count];
50 #else
51   return strchr (buf, 0);
52 #endif
53 }
54 #endif /* !__GNUC__ */
55
56 #if !__GNUC__ && !HAVE_GOOD_SPRINTF
57 int
58 nsprintf (char *buf, const char *format,...)
59 {
60   va_list args;
61
62   va_start (args, format);
63   vsprintf (buf, format, args);
64   va_end (args);
65
66   return strlen (buf);
67 }
68
69 int
70 nvsprintf (char *buf, const char *format, va_list args)
71 {
72   vsprintf (buf, format, args);
73   return strlen (buf);
74 }
75 #endif /* Not GNU C and not good sprintf(). */
76 \f
77 /* Reverses the order of NBYTES bytes at address P, thus converting
78    between little- and big-endian byte orders.  */
79 void
80 mm_reverse (void *p, size_t nbytes)
81 {
82   unsigned char *h = p, *t = &h[nbytes - 1];
83   unsigned char temp;
84
85   nbytes /= 2;
86   while (nbytes--)
87     {
88       temp = *h;
89       *h++ = *t;
90       *t-- = temp;
91     }
92 }
93
94 /* Finds the last NEEDLE of length NEEDLE_LEN in a HAYSTACK of length
95    HAYSTACK_LEN.  Returns a pointer to the needle found. */
96 char *
97 mm_find_reverse (const char *haystack, size_t haystack_len,
98          const char *needle, size_t needle_len)
99 {
100   int i;
101   for (i = haystack_len - needle_len; i >= 0; i--)
102     if (!memcmp (needle, &haystack[i], needle_len))
103       return (char *) &haystack[i];
104   return 0;
105 }
106
107 /* Compares S0 of length S0L to S1 of length S1L.  The shorter string
108    is considered to be padded with spaces to the length of the
109    longer. */
110 int
111 st_compare_pad (const char *s0, int s0l, const char *s1, int s1l)
112 {
113   /* 254 spaces. */
114   static char blanks[254] =
115   "                                                               "
116   "                                                               "
117   "                                                               "
118   "                                                               "
119   "  ";
120
121   int diff = s0l - s1l;
122   int r;
123
124   if (diff == 0)
125     {
126       if (s0l == 0)
127         return 0;
128       return memcmp (s0, s1, s0l);
129     }
130   else if (diff > 0)
131     {
132       /* s0l > s1l */
133       if (s1l)
134         {
135           r = memcmp (s0, s1, s1l);
136           if (r)
137             return r;
138         }
139       return memcmp (&s0[s1l], blanks, diff);
140     }
141   else
142     /* diff<0 */
143     {
144       /* s0l < s1l */
145       if (s0l)
146         {
147           r = memcmp (s0, s1, s0l);
148           if (r)
149             return r;
150         }
151       return memcmp (blanks, &s1[s0l], -diff);
152     }
153 }
154
155 /* Copies SRC to DEST, truncating to N characters or right-padding
156    with spaces to N characters as necessary.  Does not append a null
157    character.  SRC must be null-terminated. */
158 void
159 st_bare_pad_copy (char *dest, const char *src, size_t n)
160 {
161   size_t len;
162
163   len = strlen (src);
164   if (len >= n)
165     memcpy (dest, src, n);
166   else
167     {
168       memcpy (dest, src, len);
169       memset (&dest[len], ' ', n - len);
170     }
171 }
172
173 /* Copies SRC to DEST, truncating SRC to N characters or right-padding
174    with spaces to N characters if necessary.  Does not append a null
175    character.  SRC must be LEN characters long but does not need to be
176    null-terminated. */
177 void
178 st_bare_pad_len_copy (char *dest, const char *src, size_t n, size_t len)
179 {
180   if (len >= n)
181     memcpy (dest, src, n);
182   else
183     {
184       memcpy (dest, src, len);
185       memset (&dest[len], ' ', n - len);
186     }
187 }
188
189 /* Copies SRC to DEST, truncating SRC to N-1 characters or
190    right-padding with spaces to N-1 characters if necessary.  Always
191    appends a null character. */
192 void
193 st_pad_copy (char *dest, const char *src, size_t n)
194 {
195   size_t len;
196
197   len = strlen (src);
198   if (len == n - 1)
199     strcpy (dest, src);
200   else if (len < n - 1)
201     {
202       memcpy (dest, src, len);
203       memset (&dest[len], ' ', n - 1 - len);
204       dest[n - 1] = 0;
205     }
206   else
207     {
208       memcpy (dest, src, n - 1);
209       dest[n - 1] = 0;
210     }
211 }
212 \f
213 /* Initializes ST inside pool POOL (which may be a null pointer) with
214    initial contents S. */
215 void
216 ds_create (struct pool *pool, struct string *st, const char *s)
217 {
218   st->pool = pool;
219   st->length = strlen (s);
220   st->size = 8 + st->length * 2;
221   st->string = pool_malloc (pool, st->size + 1);
222   strcpy (st->string, s);
223 }
224
225 /* Initializes ST inside POOL (which may be null), making room for at
226    least SIZE characters. */
227 void
228 ds_init (struct pool *pool, struct string *st, size_t size)
229 {
230   st->pool = pool;
231   st->length = 0;
232   if (size > 8)
233     st->size = size;
234   else
235     st->size = 8;
236   st->string = pool_malloc (pool, st->size + 1);
237 }
238
239 /* Replaces the contents of ST with STRING.  STRING may overlap with
240    ST. */
241 void
242 ds_replace (struct string *st, const char *string)
243 {
244   char *s = st->string;
245   st->string = NULL;
246   ds_create (st->pool, st, string);
247   pool_free (st->pool, s);
248 }
249
250 /* Frees ST. */
251 void
252 ds_destroy (struct string *st)
253 {
254   pool_free (st->pool, st->string);
255 }
256
257 /* Truncates ST to zero length. */
258 void
259 ds_clear (struct string *st)
260 {
261   st->length = 0;
262 }
263
264 /* Ensures that ST can hold at least MIN_SIZE characters plus a null
265    terminator. */
266 void
267 ds_extend (struct string *st, size_t min_size)
268 {
269   if (min_size > st->size)
270     {
271       st->size *= 2;
272       if (st->size < min_size)
273         st->size = min_size * 2;
274       
275       st->string = pool_realloc (st->pool, st->string, st->size + 1);
276     }
277 }
278
279 /* Shrink ST to the minimum size need to contain its content. */
280 void
281 ds_shrink (struct string *st)
282 {
283   if (st->size != st->length)
284     {
285       st->size = st->length;
286       st->string = pool_realloc (st->pool, st->string, st->size + 1);
287     }
288 }
289
290 /* Truncates ST to at most LENGTH characters long. */
291 void
292 ds_truncate (struct string *st, size_t length)
293 {
294   if (length >= st->length)
295     return;
296   st->length = length;
297 }
298
299 /* Returns the length of ST. */
300 size_t
301 ds_length (const struct string *st)
302 {
303   return st->length;
304 }
305
306 /* Returns the allocation size of ST. */
307 size_t
308 ds_size (const struct string *st)
309 {
310   return st->size;
311 }
312
313 /* Returns the value of ST as a null-terminated string. */
314 char *
315 ds_value (const struct string *st)
316 {
317   ((char *) st->string)[st->length] = '\0';
318   return st->string;
319 }
320
321 /* Returns a pointer to the null terminator ST.
322    This might not be an actual null character unless ds_value() has
323    been called since the last modification to ST. */
324 char *
325 ds_end (const struct string *st)
326 {
327   return st->string + st->length;
328 }
329
330 /* Concatenates S onto ST. */
331 void
332 ds_concat (struct string *st, const char *s)
333 {
334   size_t s_len = strlen (s);
335   ds_extend (st, st->length + s_len);
336   strcpy (st->string + st->length, s);
337   st->length += s_len;
338 }
339
340 /* Concatenates LEN characters from BUF onto ST. */
341 void
342 ds_concat_buffer (struct string *st, const char *buf, size_t len)
343 {
344   ds_extend (st, st->length + len);
345   memcpy (st->string + st->length, buf, len);
346   st->length += len;
347 }
348
349 /* Formats FORMAT as a printf string and appends the result to ST. */
350 void
351 ds_printf (struct string *st, const char *format, ...)
352 {
353   /* Fscking glibc silently changed behavior between 2.0 and 2.1.
354      Fsck fsck fsck.  Before, it returned -1 on buffer overflow.  Now,
355      it returns the number of characters (not bytes) that would have
356      been written. */
357   va_list args;
358
359   int avail, needed;
360
361   va_start (args, format);
362   avail = st->size - st->length + 1;
363   needed = vsnprintf (st->string + st->length, avail, format, args);
364   va_end (args);
365
366   if (needed >= avail)
367     {
368       ds_extend (st, st->length + needed);
369       
370       va_start (args, format);
371       vsprintf (st->string + st->length, format, args);
372       va_end (args);
373     }
374   else
375     while (needed == -1)
376       {
377         ds_extend (st, (st->size + 1) * 2);
378         avail = st->size - st->length + 1;
379         
380         va_start (args, format);
381         needed = vsnprintf (st->string + st->length, avail, format, args);
382         va_end (args);
383       }
384
385   st->length += needed;
386 }
387
388 /* Appends character CH to ST. */
389 void
390 ds_putchar (struct string *st, int ch)
391 {
392   if (st->length == st->size)
393     ds_extend (st, st->length + 1);
394   st->string[st->length++] = ch;
395 }
396
397 /* Reads a newline-terminated line from STREAM into ST.
398    Newline is the last character of ST on return, unless an I/O error
399    or end of file is encountered after reading some characters.
400    Returns 1 if a line is successfully read, or 0 if no characters at
401    all were read before an I/O error or end of file was
402    encountered. */
403 int
404 ds_getline (struct string *st, FILE *stream)
405 {
406   int c;
407
408   c = getc (stream);
409   if (c == EOF)
410     return 0;
411
412   for (;;)
413     {
414       ds_putchar (st, c);
415       if (c == '\n')
416         return 1;
417
418       c = getc (stream);
419       if (c == EOF)
420         return 1;
421     }
422 }
423
424 /* Reads a line from STREAM into ST, then preprocesses as follows:
425
426    - Splices lines terminated with `\'.
427
428    - Deletes comments introduced by `#' outside of single or double
429      quotes.
430
431    - Trailing whitespace will be deleted.  
432
433    Increments cust_ln as appropriate.
434
435    Returns nonzero only if a line was successfully read. */
436 int
437 ds_get_config_line (FILE *stream, struct string *st, struct file_locator *where)
438 {
439   /* Read the first line. */
440   ds_clear (st);
441   where->line_number++;
442   if (!ds_getline (st, stream))
443     return 0;
444
445   /* Read additional lines, if any. */
446   for (;;)
447     {
448       /* Remove trailing whitespace. */
449       {
450         char *s = ds_value (st);
451         size_t len = ds_length (st);
452       
453         while (len > 0 && isspace ((unsigned char) s[len - 1]))
454           len--;
455         ds_truncate (st, len);
456       }
457
458       /* Check for trailing \.  Remove if found, bail otherwise. */
459       if (ds_length (st) == 0 || ds_value (st)[ds_length (st) - 1] != '\\')
460         break;
461       ds_truncate (st, ds_length (st) - 1);
462
463       /* Append another line and go around again. */
464       {
465         int success = ds_getline (st, stream);
466         where->line_number++;
467         if (!success)
468           return 1;
469       }
470     }
471
472   /* Find a comment and remove. */
473   {
474     char *cp;
475     int quote = 0;
476       
477     for (cp = ds_value (st); *cp; cp++)
478       if (quote)
479         {
480           if (*cp == quote)
481             quote = 0;
482           else if (*cp == '\\')
483             cp++;
484         }
485       else if (*cp == '\'' || *cp == '"')
486         quote = *cp;
487       else if (*cp == '#')
488         {
489           ds_truncate (st, cp - ds_value (st));
490           break;
491         }
492   }
493
494   return 1;
495 }
496 \f
497 /* Lengthed strings. */
498
499 /* Creates a new lengthed string LS in POOL with contents as a copy of
500    S. */
501 void
502 ls_create (struct pool *pool, struct len_string *ls, const char *s)
503 {
504   ls->length = strlen (s);
505   ls->string = pool_alloc (pool, ls->length + 1);
506   memcpy (ls->string, s, ls->length + 1);
507 }
508
509 /* Creates a new lengthed string LS in POOL with contents as a copy of
510    BUFFER with length LEN. */
511 void
512 ls_create_buffer (struct pool *pool, struct len_string *ls,
513                   const char *buffer, size_t len)
514 {
515   ls->length = len;
516   ls->string = pool_malloc (pool, len + 1);
517   memcpy (ls->string, buffer, len);
518   ls->string[len] = '\0';
519 }
520
521 /* Sets the fields of LS to the specified values. */
522 void
523 ls_init (struct len_string *ls, const char *string, size_t length)
524 {
525   ls->string = (char *) string;
526   ls->length = length;
527 }
528
529 /* Copies the fields of SRC to DST. */
530 void
531 ls_shallow_copy (struct len_string *dst, const struct len_string *src)
532 {
533   *dst = *src;
534 }
535
536 /* Frees the memory in POOL backing LS. */
537 void
538 ls_destroy (struct pool *pool, struct len_string *ls)
539 {
540   pool_free (pool, ls->string);
541 }
542
543 /* Sets LS to a null pointer value. */
544 void
545 ls_null (struct len_string *ls)
546 {
547   ls->string = NULL;
548 }
549
550 /* Returns nonzero only if LS has a null pointer value. */
551 int
552 ls_null_p (const struct len_string *ls)
553 {
554   return ls->string == NULL;
555 }
556
557 /* Returns nonzero only if LS is a null pointer or has length 0. */
558 int
559 ls_empty_p (const struct len_string *ls)
560 {
561   return ls->string == NULL || ls->length == 0;
562 }
563
564 /* Returns the length of LS, which must not be null. */
565 size_t
566 ls_length (const struct len_string *ls)
567 {
568   return ls->length;
569 }
570
571 /* Returns a pointer to the character string in LS. */
572 char *
573 ls_value (const struct len_string *ls)
574 {
575   return (char *) ls->string;
576 }
577
578 /* Returns a pointer to the null terminator of the character string in
579    LS. */
580 char *
581 ls_end (const struct len_string *ls)
582 {
583   return (char *) (ls->string + ls->length);
584 }