e807f36dfa803ea1c393737e415a932964b9a93a
[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 "str.h"
22 #include "error.h"
23 #include <ctype.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include "alloc.h"
27 #include "error.h"
28 #include "pool.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;
335
336   if (!s) return;
337
338   s_len = strlen (s);
339   ds_extend (st, st->length + s_len);
340   strcpy (st->string + st->length, s);
341   st->length += s_len;
342 }
343
344 /* Concatenates LEN characters from BUF onto ST. */
345 void
346 ds_concat_buffer (struct string *st, const char *buf, size_t len)
347 {
348   ds_extend (st, st->length + len);
349   memcpy (st->string + st->length, buf, len);
350   st->length += len;
351 }
352
353 void ds_vprintf (struct string *st, const char *format, va_list args);
354
355
356 /* Formats FORMAT as a printf string and appends the result to ST. */
357 void
358 ds_printf (struct string *st, const char *format, ...)
359 {
360   va_list args;
361
362   va_start (args, format);
363   ds_vprintf(st,format,args);
364   va_end (args);
365
366 }
367
368 /* Formats FORMAT as a printf string and appends the result to ST. */
369 void
370 ds_vprintf (struct string *st, const char *format, va_list args)
371 {
372   /* Fscking glibc silently changed behavior between 2.0 and 2.1.
373      Fsck fsck fsck.  Before, it returned -1 on buffer overflow.  Now,
374      it returns the number of characters (not bytes) that would have
375      been written. */
376
377   int avail, needed;
378
379   avail = st->size - st->length + 1;
380   needed = vsnprintf (st->string + st->length, avail, format, args);
381
382
383   if (needed >= avail)
384     {
385       ds_extend (st, st->length + needed);
386       
387       vsprintf (st->string + st->length, format, args);
388     }
389   else
390     while (needed == -1)
391       {
392         ds_extend (st, (st->size + 1) * 2);
393         avail = st->size - st->length + 1;
394
395         needed = vsnprintf (st->string + st->length, avail, format, args);
396
397       }
398
399   st->length += needed;
400 }
401
402 /* Appends character CH to ST. */
403 void
404 ds_putchar (struct string *st, int ch)
405 {
406   if (st->length == st->size)
407     ds_extend (st, st->length + 1);
408   st->string[st->length++] = ch;
409 }
410
411 /* Reads a newline-terminated line from STREAM into ST.
412    Newline is the last character of ST on return, unless an I/O error
413    or end of file is encountered after reading some characters.
414    Returns 1 if a line is successfully read, or 0 if no characters at
415    all were read before an I/O error or end of file was
416    encountered. */
417 int
418 ds_getline (struct string *st, FILE *stream)
419 {
420   int c;
421
422   c = getc (stream);
423   if (c == EOF)
424     return 0;
425
426   for (;;)
427     {
428       ds_putchar (st, c);
429       if (c == '\n')
430         return 1;
431
432       c = getc (stream);
433       if (c == EOF)
434         return 1;
435     }
436 }
437
438 /* Reads a line from STREAM into ST, then preprocesses as follows:
439
440    - Splices lines terminated with `\'.
441
442    - Deletes comments introduced by `#' outside of single or double
443      quotes.
444
445    - Trailing whitespace will be deleted.  
446
447    Increments cust_ln as appropriate.
448
449    Returns nonzero only if a line was successfully read. */
450 int
451 ds_get_config_line (FILE *stream, struct string *st, struct file_locator *where)
452 {
453   /* Read the first line. */
454   ds_clear (st);
455   where->line_number++;
456   if (!ds_getline (st, stream))
457     return 0;
458
459   /* Read additional lines, if any. */
460   for (;;)
461     {
462       /* Remove trailing whitespace. */
463       {
464         char *s = ds_value (st);
465         size_t len = ds_length (st);
466       
467         while (len > 0 && isspace ((unsigned char) s[len - 1]))
468           len--;
469         ds_truncate (st, len);
470       }
471
472       /* Check for trailing \.  Remove if found, bail otherwise. */
473       if (ds_length (st) == 0 || ds_value (st)[ds_length (st) - 1] != '\\')
474         break;
475       ds_truncate (st, ds_length (st) - 1);
476
477       /* Append another line and go around again. */
478       {
479         int success = ds_getline (st, stream);
480         where->line_number++;
481         if (!success)
482           return 1;
483       }
484     }
485
486   /* Find a comment and remove. */
487   {
488     char *cp;
489     int quote = 0;
490       
491     for (cp = ds_value (st); *cp; cp++)
492       if (quote)
493         {
494           if (*cp == quote)
495             quote = 0;
496           else if (*cp == '\\')
497             cp++;
498         }
499       else if (*cp == '\'' || *cp == '"')
500         quote = *cp;
501       else if (*cp == '#')
502         {
503           ds_truncate (st, cp - ds_value (st));
504           break;
505         }
506   }
507
508   return 1;
509 }
510 \f
511 /* Lengthed strings. */
512
513 /* Creates a new lengthed string LS in POOL with contents as a copy of
514    S. */
515 void
516 ls_create (struct pool *pool, struct len_string *ls, const char *s)
517 {
518   ls->length = strlen (s);
519   ls->string = pool_alloc (pool, ls->length + 1);
520   memcpy (ls->string, s, ls->length + 1);
521 }
522
523 /* Creates a new lengthed string LS in POOL with contents as a copy of
524    BUFFER with length LEN. */
525 void
526 ls_create_buffer (struct pool *pool, struct len_string *ls,
527                   const char *buffer, size_t len)
528 {
529   ls->length = len;
530   ls->string = pool_malloc (pool, len + 1);
531   memcpy (ls->string, buffer, len);
532   ls->string[len] = '\0';
533 }
534
535 /* Sets the fields of LS to the specified values. */
536 void
537 ls_init (struct len_string *ls, const char *string, size_t length)
538 {
539   ls->string = (char *) string;
540   ls->length = length;
541 }
542
543 /* Copies the fields of SRC to DST. */
544 void
545 ls_shallow_copy (struct len_string *dst, const struct len_string *src)
546 {
547   *dst = *src;
548 }
549
550 /* Frees the memory in POOL backing LS. */
551 void
552 ls_destroy (struct pool *pool, struct len_string *ls)
553 {
554   pool_free (pool, ls->string);
555 }
556
557 /* Sets LS to a null pointer value. */
558 void
559 ls_null (struct len_string *ls)
560 {
561   ls->string = NULL;
562 }
563
564 /* Returns nonzero only if LS has a null pointer value. */
565 int
566 ls_null_p (const struct len_string *ls)
567 {
568   return ls->string == NULL;
569 }
570
571 /* Returns nonzero only if LS is a null pointer or has length 0. */
572 int
573 ls_empty_p (const struct len_string *ls)
574 {
575   return ls->string == NULL || ls->length == 0;
576 }
577
578 /* Returns the length of LS, which must not be null. */
579 size_t
580 ls_length (const struct len_string *ls)
581 {
582   return ls->length;
583 }
584
585 /* Returns a pointer to the character string in LS. */
586 char *
587 ls_value (const struct len_string *ls)
588 {
589   return (char *) ls->string;
590 }
591
592 /* Returns a pointer to the null terminator of the character string in
593    LS. */
594 char *
595 ls_end (const struct len_string *ls)
596 {
597   return (char *) (ls->string + ls->length);
598 }