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