Add va_copy.h header instead of inlining a definition.
[pspp-builds.git] / src / libpspp / str.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21
22 #include "str.h"
23
24 #include <ctype.h>
25 #include <limits.h>
26 #include <stdlib.h>
27
28 #include <libpspp/va_copy.h>
29 #include <libpspp/alloc.h>
30 #include <libpspp/message.h>
31
32 #include "minmax.h"
33 #include "size_max.h"
34 \f
35 /* Reverses the order of NBYTES bytes at address P, thus converting
36    between little- and big-endian byte orders.  */
37 void
38 buf_reverse (char *p, size_t nbytes)
39 {
40   char *h = p, *t = &h[nbytes - 1];
41   char temp;
42
43   nbytes /= 2;
44   while (nbytes--)
45     {
46       temp = *h;
47       *h++ = *t;
48       *t-- = temp;
49     }
50 }
51
52 /* Finds the last NEEDLE of length NEEDLE_LEN in a HAYSTACK of length
53    HAYSTACK_LEN.  Returns a pointer to the needle found. */
54 char *
55 buf_find_reverse (const char *haystack, size_t haystack_len,
56                  const char *needle, size_t needle_len)
57 {
58   int i;
59   for (i = haystack_len - needle_len; i >= 0; i--)
60     if (!memcmp (needle, &haystack[i], needle_len))
61       return (char *) &haystack[i];
62   return 0;
63 }
64
65 /* Compares the SIZE bytes in A to those in B, disregarding case,
66    and returns a strcmp()-type result. */
67 int
68 buf_compare_case (const char *a_, const char *b_, size_t size)
69 {
70   const unsigned char *a = (unsigned char *) a_;
71   const unsigned char *b = (unsigned char *) b_;
72
73   while (size-- > 0) 
74     {
75       unsigned char ac = toupper (*a++);
76       unsigned char bc = toupper (*b++);
77
78       if (ac != bc) 
79         return ac > bc ? 1 : -1;
80     }
81
82   return 0;
83 }
84
85 /* Compares A of length A_LEN to B of length B_LEN.  The shorter
86    string is considered to be padded with spaces to the length of
87    the longer. */
88 int
89 buf_compare_rpad (const char *a, size_t a_len, const char *b, size_t b_len)
90 {
91   size_t min_len;
92   int result;
93
94   min_len = a_len < b_len ? a_len : b_len;
95   result = memcmp (a, b, min_len);
96   if (result != 0)
97     return result;
98   else 
99     {
100       size_t idx;
101       
102       if (a_len < b_len) 
103         {
104           for (idx = min_len; idx < b_len; idx++)
105             if (' ' != b[idx])
106               return ' ' > b[idx] ? 1 : -1;
107         }
108       else 
109         {
110           for (idx = min_len; idx < a_len; idx++)
111             if (a[idx] != ' ')
112               return a[idx] > ' ' ? 1 : -1;
113         }
114       return 0;
115     }
116 }
117
118 /* Compares strin A to string B.  The shorter string is
119    considered to be padded with spaces to the length of the
120    longer. */
121 int
122 str_compare_rpad (const char *a, const char *b)
123 {
124   return buf_compare_rpad (a, strlen (a), b, strlen (b));
125 }
126
127 /* Copies string SRC to buffer DST, of size DST_SIZE bytes.
128    DST is truncated to DST_SIZE bytes or padded on the right with
129    spaces as needed. */
130 void
131 buf_copy_str_rpad (char *dst, size_t dst_size, const char *src)
132 {
133   size_t src_len = strlen (src);
134   if (src_len >= dst_size)
135     memcpy (dst, src, dst_size);
136   else
137     {
138       memcpy (dst, src, src_len);
139       memset (&dst[src_len], ' ', dst_size - src_len);
140     }
141 }
142
143 /* Copies string SRC to buffer DST, of size DST_SIZE bytes.
144    DST is truncated to DST_SIZE bytes or padded on the left with
145    spaces as needed. */
146 void
147 buf_copy_str_lpad (char *dst, size_t dst_size, const char *src)
148 {
149   size_t src_len = strlen (src);
150   if (src_len >= dst_size)
151     memcpy (dst, src, dst_size);
152   else
153     {
154       size_t pad_cnt = dst_size - src_len;
155       memset (&dst[0], ' ', pad_cnt);
156       memcpy (dst + pad_cnt, src, src_len);
157     }
158 }
159
160 /* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes.
161    DST is truncated to DST_SIZE bytes or padded on the right with
162    spaces as needed. */
163 void
164 buf_copy_rpad (char *dst, size_t dst_size,
165                const char *src, size_t src_size)
166 {
167   if (src_size >= dst_size)
168     memmove (dst, src, dst_size);
169   else
170     {
171       memmove (dst, src, src_size);
172       memset (&dst[src_size], ' ', dst_size - src_size);
173     }
174 }
175
176 /* Copies string SRC to string DST, which is in a buffer DST_SIZE
177    bytes long.
178    Truncates DST to DST_SIZE - 1 characters or right-pads with
179    spaces to DST_SIZE - 1 characters if necessary. */
180 void
181 str_copy_rpad (char *dst, size_t dst_size, const char *src)
182 {
183   size_t src_len = strlen (src);
184   if (src_len < dst_size - 1)
185     {
186       memcpy (dst, src, src_len);
187       memset (&dst[src_len], ' ', dst_size - 1 - src_len);
188     }
189   else
190     memcpy (dst, src, dst_size - 1);
191   dst[dst_size - 1] = 0;
192 }
193
194 /* Copies SRC to DST, which is in a buffer DST_SIZE bytes long.
195    Truncates DST to DST_SIZE - 1 characters, if necessary. */
196 void
197 str_copy_trunc (char *dst, size_t dst_size, const char *src) 
198 {
199   size_t src_len = strlen (src);
200   assert (dst_size > 0);
201   if (src_len + 1 < dst_size)
202     memcpy (dst, src, src_len + 1);
203   else 
204     {
205       memcpy (dst, src, dst_size - 1);
206       dst[dst_size - 1] = '\0';
207     }
208 }
209
210 /* Copies buffer SRC, of SRC_LEN bytes,
211    to DST, which is in a buffer DST_SIZE bytes long.
212    Truncates DST to DST_SIZE - 1 characters, if necessary. */
213 void
214 str_copy_buf_trunc (char *dst, size_t dst_size,
215                     const char *src, size_t src_size) 
216 {
217   size_t dst_len;
218   assert (dst_size > 0);
219
220   dst_len = src_size < dst_size ? src_size : dst_size - 1;
221   memcpy (dst, src, dst_len);
222   dst[dst_len] = '\0';
223 }
224
225 /* Converts each character in S to uppercase. */
226 void
227 str_uppercase (char *s) 
228 {
229   for (; *s != '\0'; s++)
230     *s = toupper ((unsigned char) *s);
231 }
232
233 /* Converts each character in S to lowercase. */
234 void
235 str_lowercase (char *s) 
236 {
237   for (; *s != '\0'; s++)
238     *s = tolower ((unsigned char) *s);
239 }
240 \f
241 /* Initializes ST with initial contents S. */
242 void
243 ds_create (struct string *st, const char *s)
244 {
245   st->length = strlen (s);
246   st->capacity = MAX (8, st->length * 2);
247   st->string = xmalloc (st->capacity + 1);
248   strcpy (st->string, s);
249 }
250
251 /* Initializes ST, making room for at least CAPACITY characters. */
252 void
253 ds_init (struct string *st, size_t capacity)
254 {
255   st->length = 0;
256   st->capacity = MAX (8, capacity);
257   st->string = xmalloc (st->capacity + 1);
258 }
259
260 /* Frees ST. */
261 void
262 ds_destroy (struct string *st)
263 {
264   if (st != NULL) 
265     {
266       free (st->string);
267       st->string = NULL;
268       st->length = 0;
269       st->capacity = 0; 
270     }
271 }
272
273 /* Swaps the contents of strings A and B. */
274 void
275 ds_swap (struct string *a, struct string *b) 
276 {
277   struct string tmp = *a;
278   *a = *b;
279   *b = tmp;
280 }
281
282 /* Initializes DST with the CNT characters from SRC starting at
283    position IDX. */
284 void
285 ds_init_substring (struct string *dst,
286                    const struct string *src, size_t idx, size_t cnt)
287 {
288   assert (dst != src);
289   ds_init (dst, cnt);
290   ds_assign_substring (dst, src, idx, cnt);
291 }
292
293 /* Copies SRC into DST.
294    DST and SRC may be the same string. */
295 void
296 ds_assign_string (struct string *dst, const struct string *src) 
297 {
298   ds_assign_buffer (dst, ds_data (src), ds_length (src));
299 }
300
301 /* Replaces DST by CNT characters from SRC starting at position
302    IDX.
303    DST and SRC may be the same string. */
304 void
305 ds_assign_substring (struct string *dst,
306                      const struct string *src, size_t idx, size_t cnt) 
307 {
308   if (idx < src->length)
309     ds_assign_buffer (dst, src->string + idx, MIN (cnt, src->length - idx));
310   else 
311     ds_clear (dst);
312 }
313
314 /* Replaces DST by the LENGTH characters in SRC.
315    SRC may be a substring within DST. */
316 void
317 ds_assign_buffer (struct string *dst, const char *src, size_t length)
318 {
319   dst->length = length;
320   ds_extend (dst, length);
321   memmove (dst->string, src, length);
322 }
323
324 /* Replaces DST by null-terminated string SRC.  SRC may overlap
325    with DST. */
326 void
327 ds_assign_c_str (struct string *dst, const char *src)
328 {
329   ds_assign_buffer (dst, src, strlen (src));
330 }
331
332 /* Truncates ST to zero length. */
333 void
334 ds_clear (struct string *st)
335 {
336   st->length = 0;
337 }
338
339 /* Ensures that ST can hold at least MIN_CAPACITY characters plus a null
340    terminator. */
341 void
342 ds_extend (struct string *st, size_t min_capacity)
343 {
344   if (min_capacity > st->capacity)
345     {
346       st->capacity *= 2;
347       if (st->capacity < min_capacity)
348         st->capacity = 2 * min_capacity;
349
350       st->string = xrealloc (st->string, st->capacity + 1);
351     }
352 }
353
354 /* Shrink ST to the minimum capacity need to contain its content. */
355 void
356 ds_shrink (struct string *st)
357 {
358   if (st->capacity != st->length)
359     {
360       st->capacity = st->length;
361       st->string = xrealloc (st->string, st->capacity + 1);
362     }
363 }
364
365 /* Truncates ST to at most LENGTH characters long. */
366 void
367 ds_truncate (struct string *st, size_t length)
368 {
369   if (st->length > length)
370     st->length = length;
371 }
372
373 /* Pad ST on the right with copies of PAD until ST is at least
374    LENGTH characters in size.  If ST is initially LENGTH
375    characters or longer, this is a no-op. */
376 void
377 ds_rpad (struct string *st, size_t length, char pad) 
378 {
379   if (length > st->length)
380     ds_putc_multiple (st, pad, length - st->length);
381 }
382
383 /* Removes trailing spaces from ST.
384    Returns number of spaces removed. */
385 int
386 ds_rtrim_spaces (struct string *st) 
387 {
388   int cnt = 0;
389   while (isspace (ds_last (st))) 
390     {
391       st->length--;
392       cnt++;
393     }
394   return cnt;
395 }
396
397 /* Removes leading spaces from ST.
398    Returns number of spaces removed. */
399 int
400 ds_ltrim_spaces (struct string *st) 
401 {
402   size_t cnt = 0;
403   while (isspace (ds_at (st, cnt)))
404     cnt++;
405   if (cnt > 0)
406     ds_assign_substring (st, st, cnt, SIZE_MAX);
407   return cnt;
408 }
409
410 /* Trims leading and trailing spaces from ST. */
411 void
412 ds_trim_spaces (struct string *st) 
413 {
414   ds_rtrim_spaces (st);
415   ds_ltrim_spaces (st);
416 }
417
418 /* If the last character in ST is C, removes it and returns true.
419    Otherwise, returns false without modifying ST. */
420 bool
421 ds_chomp (struct string *st, char c_) 
422 {
423   unsigned char c = c_;
424   if (ds_last (st) == c)
425     {
426       st->length--;
427       return true;
428     }
429   else
430     return false;
431 }
432
433 /* Divides ST into tokens separated by any of the DELIMITERS.
434    Each call replaces TOKEN by the next token in ST, or by an
435    empty string if no tokens remain.  Returns true if a token was
436    obtained, false otherwise.
437
438    Before the first call, initialize *SAVE_IDX to 0.  Do not
439    modify *SAVE_IDX between calls.
440
441    ST divides into exactly one more tokens than it contains
442    delimiters.  That is, a delimiter at the start or end of ST or
443    a pair of adjacent delimiters yields an empty token, and the
444    empty string contains a single token. */
445 bool
446 ds_separate (const struct string *st, struct string *token,
447              const char *delimiters, size_t *save_idx)
448 {
449   if (*save_idx <= ds_length (st))
450     {
451       size_t length = ds_cspan (st, *save_idx, delimiters);
452       ds_assign_substring (token, st, *save_idx, length);
453       *save_idx += length + 1;
454       return true;
455     }
456   else 
457     return false;
458 }
459
460 /* Divides ST into tokens separated by any of the DELIMITERS,
461    merging adjacent delimiters so that the empty string is never
462    produced as a token.  Each call replaces TOKEN by the next
463    token in ST, or by an empty string if no tokens remain.
464    Returns true if a token was obtained, false otherwise.
465
466    Before the first call, initialize *SAVE_IDX to 0.  Do not
467    modify *SAVE_IDX between calls. */
468 bool
469 ds_tokenize (const struct string *st, struct string *token,
470              const char *delimiters, size_t *save_idx)
471 {
472   size_t start = *save_idx + ds_span (st, *save_idx, delimiters);
473   size_t length = ds_cspan (st, start, delimiters);
474   ds_assign_substring (token, st, start, length);
475   *save_idx = start + length;
476   return length > 0;
477 }
478
479 /* Returns true if ST is empty, false otherwise. */
480 bool
481 ds_is_empty (const struct string *st) 
482 {
483   return st->length == 0;
484 }
485
486 /* Returns the length of ST. */
487 size_t
488 ds_length (const struct string *st)
489 {
490   return st->length;
491 }
492
493 /* Returns the value of ST as a null-terminated string. */
494 char *
495 ds_c_str (const struct string *st_)
496 {
497   struct string *st = (struct string *) st_;
498   if (st->string == NULL) 
499     ds_extend (st, 1);
500   st->string[st->length] = '\0';
501   return st->string;
502 }
503
504 /* Returns the string data inside ST. */
505 char *
506 ds_data (const struct string *st)
507 {
508   return st->string;
509 }
510
511 /* Returns a pointer to the null terminator ST.
512    This might not be an actual null character unless ds_c_str() has
513    been called since the last modification to ST. */
514 char *
515 ds_end (const struct string *st)
516 {
517   return st->string + st->length;
518 }
519
520 /* Returns the allocation size of ST. */
521 size_t
522 ds_capacity (const struct string *st)
523 {
524   return st->capacity;
525 }
526
527 /* Returns the character in position IDX in ST, as a value in the
528    range of unsigned char.  Returns EOF if IDX is out of the
529    range of indexes for ST. */
530 int
531 ds_at (const struct string *st, size_t idx) 
532 {
533   return idx < st->length ? (unsigned char) st->string[idx] : EOF;
534 }
535
536 /* Returns the first character in ST as a value in the range of
537    unsigned char.  Returns EOF if ST is the empty string. */
538 int
539 ds_first (const struct string *st) 
540 {
541   return ds_at (st, 0);
542 }
543
544 /* Returns the last character in ST as a value in the range of
545    unsigned char.  Returns EOF if ST is the empty string. */
546 int
547 ds_last (const struct string *st) 
548 {
549   return st->length > 0 ? (unsigned char) st->string[st->length - 1] : EOF;
550 }
551
552 /* Returns the number of consecutive characters starting at OFS
553    in ST that are in SKIP_SET.  (The null terminator is not
554    considered to be part of SKIP_SET.) */
555 size_t
556 ds_span (const struct string *st, size_t ofs, const char skip_set[])
557 {
558   size_t i;
559   for (i = ofs; i < st->length; i++) 
560     {
561       int c = st->string[i];
562       if (strchr (skip_set, c) == NULL || c == '\0')
563         break; 
564     }
565   return i - ofs;
566 }
567
568 /* Returns the number of consecutive characters starting at OFS
569    in ST that are not in STOP_SET.  (The null terminator is not
570    considered to be part of STOP_SET.) */
571 size_t
572 ds_cspan (const struct string *st, size_t ofs, const char stop_set[])
573 {
574   size_t i;
575   for (i = ofs; i < st->length; i++) 
576     {
577       int c = st->string[i];
578       if (strchr (stop_set, c) != NULL)
579         break; 
580     }
581   return i - ofs;
582 }
583
584 /* Appends to ST a newline-terminated line read from STREAM.
585    Newline is the last character of ST on return, unless an I/O error
586    or end of file is encountered after reading some characters.
587    Returns true if a line is successfully read, false if no characters at
588    all were read before an I/O error or end of file was
589    encountered. */
590 bool
591 ds_gets (struct string *st, FILE *stream)
592 {
593   int c;
594
595   c = getc (stream);
596   if (c == EOF)
597     return false;
598
599   for (;;)
600     {
601       ds_putc (st, c);
602       if (c == '\n')
603         return true;
604
605       c = getc (stream);
606       if (c == EOF)
607         return true;
608     }
609 }
610
611 /* Removes a comment introduced by `#' from ST,
612    ignoring occurrences inside quoted strings. */
613 static void
614 remove_comment (struct string *st)
615 {
616   char *cp;
617   int quote = 0;
618       
619   for (cp = ds_c_str (st); cp < ds_end (st); cp++)
620     if (quote)
621       {
622         if (*cp == quote)
623           quote = 0;
624         else if (*cp == '\\')
625           cp++;
626       }
627     else if (*cp == '\'' || *cp == '"')
628       quote = *cp;
629     else if (*cp == '#')
630       {
631         ds_truncate (st, cp - ds_c_str (st));
632         break;
633       }
634 }
635
636 /* Reads a line from STREAM into ST, then preprocesses as follows:
637
638    - Splices lines terminated with `\'.
639
640    - Deletes comments introduced by `#' outside of single or double
641      quotes.
642
643    - Deletes trailing white space.  
644
645    Returns true if a line was successfully read, false on
646    failure.  If LINE_NUMBER is non-null, then *LINE_NUMBER is
647    incremented by the number of lines read. */
648 bool
649 ds_get_config_line (FILE *stream, struct string *st, int *line_number)
650 {
651   ds_clear (st);
652   do
653     {
654       if (!ds_gets (st, stream))
655         return false;
656       (*line_number)++;
657       ds_rtrim_spaces (st);
658     }
659   while (ds_chomp (st, '\\'));
660  
661   remove_comment (st);
662   return true;
663 }
664
665 /* Concatenates S onto ST. */
666 void
667 ds_puts (struct string *st, const char *s)
668 {
669   size_t s_len;
670
671   if (!s) return;
672
673   s_len = strlen (s);
674   ds_extend (st, st->length + s_len);
675   strcpy (st->string + st->length, s);
676   st->length += s_len;
677 }
678
679 /* Concatenates LEN characters from BUF onto ST. */
680 void
681 ds_concat (struct string *st, const char *buf, size_t len)
682 {
683   ds_extend (st, st->length + len);
684   memcpy (st->string + st->length, buf, len);
685   st->length += len;
686 }
687
688 /* Returns ds_end(ST) and THEN increases the length by INCR. */
689 char *
690 ds_append_uninit(struct string *st, size_t incr)
691 {
692   char *end;
693
694   ds_extend(st, ds_length(st) + incr);
695
696   end = ds_end(st);
697
698   st->length += incr;
699  
700   return end;
701 }
702
703 /* Formats FORMAT as a printf string and appends the result to ST. */
704 void
705 ds_printf (struct string *st, const char *format, ...)
706 {
707   va_list args;
708
709   va_start (args, format);
710   ds_vprintf(st, format, args);
711   va_end (args);
712 }
713
714 /* Formats FORMAT as a printf string and appends the result to ST. */
715 void
716 ds_vprintf (struct string *st, const char *format, va_list args_)
717 {
718   int avail, needed;
719   va_list args;
720
721   va_copy (args, args_);
722   avail = st->string != NULL ? st->capacity - st->length + 1 : 0;
723   needed = vsnprintf (st->string + st->length, avail, format, args);
724   va_end (args);
725
726   if (needed >= avail)
727     {
728       ds_extend (st, st->length + needed);
729       
730       va_copy (args, args_);
731       vsprintf (st->string + st->length, format, args);
732       va_end (args);
733     }
734   else 
735     {
736       /* Some old libc's returned -1 when the destination string
737          was too short. */
738       while (needed == -1)
739         {
740           ds_extend (st, (st->capacity + 1) * 2);
741           avail = st->capacity - st->length + 1;
742
743           va_copy (args, args_);
744           needed = vsnprintf (st->string + st->length, avail, format, args);
745           va_end (args);
746         } 
747     }
748
749   st->length += needed;
750 }
751
752 /* Appends character CH to ST. */
753 void
754 ds_putc (struct string *st, int ch)
755 {
756   if (st->length >= st->capacity)
757     ds_extend (st, st->length + 1);
758   st->string[st->length++] = ch;
759 }
760
761 /* Appends CNT copies of character CH to ST. */
762 void
763 ds_putc_multiple (struct string *st, int ch, size_t cnt) 
764 {
765   ds_extend (st, st->length + cnt);
766   memset (&st->string[st->length], ch, cnt);
767   st->length += cnt;
768 }
769
770 \f
771 /* Lengthed strings. */
772
773 /* Creates a new lengthed string LS with contents as a copy of
774    S. */
775 void
776 ls_create (struct fixed_string *ls, const char *s)
777 {
778   ls->length = strlen (s);
779   ls->string = xmalloc (ls->length + 1);
780   memcpy (ls->string, s, ls->length + 1);
781 }
782
783 /* Creates a new lengthed string LS with contents as a copy of
784    BUFFER with length LEN. */
785 void
786 ls_create_buffer (struct fixed_string *ls,
787                   const char *buffer, size_t len)
788 {
789   ls->length = len;
790   ls->string = xmalloc (len + 1);
791   memcpy (ls->string, buffer, len);
792   ls->string[len] = '\0';
793 }
794
795 /* Sets the fields of LS to the specified values. */
796 void
797 ls_init (struct fixed_string *ls, const char *string, size_t length)
798 {
799   ls->string = (char *) string;
800   ls->length = length;
801 }
802
803 /* Copies the fields of SRC to DST. */
804 void
805 ls_shallow_copy (struct fixed_string *dst, const struct fixed_string *src)
806 {
807   *dst = *src;
808 }
809
810 /* Frees the memory backing LS. */
811 void
812 ls_destroy (struct fixed_string *ls)
813 {
814   free (ls->string);
815 }
816
817 /* Sets LS to a null pointer value. */
818 void
819 ls_null (struct fixed_string *ls)
820 {
821   ls->string = NULL;
822 }
823
824 /* Returns nonzero only if LS has a null pointer value. */
825 int
826 ls_null_p (const struct fixed_string *ls)
827 {
828   return ls->string == NULL;
829 }
830
831 /* Returns nonzero only if LS is a null pointer or has length 0. */
832 int
833 ls_empty_p (const struct fixed_string *ls)
834 {
835   return ls->string == NULL || ls->length == 0;
836 }
837
838 /* Returns the length of LS, which must not be null. */
839 size_t
840 ls_length (const struct fixed_string *ls)
841 {
842   return ls->length;
843 }
844
845 /* Returns a pointer to the character string in LS. */
846 char *
847 ls_c_str (const struct fixed_string *ls)
848 {
849   return (char *) ls->string;
850 }
851
852 /* Returns a pointer to the null terminator of the character string in
853    LS. */
854 char *
855 ls_end (const struct fixed_string *ls)
856 {
857   return (char *) (ls->string + ls->length);
858 }