str: Make ss_alloc_substring() allocate null-terminated strings.
[pspp] / src / libpspp / str.c
index b88493d47fabafff46fd36a724cdcc1ad3e26d68..9f0cb6e31463ab5caf319114e43fed26f640ae2b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2009, 2010 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <stdint.h>
 #include <stdlib.h>
 
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 #include <libpspp/pool.h>
 
+#include <relocatable.h>
 #include "minmax.h"
 #include "xalloc.h"
+#include "xmemdup0.h"
 #include "xsize.h"
 \f
 /* Reverses the order of NBYTES bytes at address P, thus converting
@@ -124,9 +127,9 @@ str_compare_rpad (const char *a, const char *b)
 
 /* Copies string SRC to buffer DST, of size DST_SIZE bytes.
    DST is truncated to DST_SIZE bytes or padded on the right with
-   spaces as needed. */
+   copies of PAD as needed. */
 void
-buf_copy_str_rpad (char *dst, size_t dst_size, const char *src)
+buf_copy_str_rpad (char *dst, size_t dst_size, const char *src, char pad)
 {
   size_t src_len = strlen (src);
   if (src_len >= dst_size)
@@ -134,15 +137,15 @@ buf_copy_str_rpad (char *dst, size_t dst_size, const char *src)
   else
     {
       memcpy (dst, src, src_len);
-      memset (&dst[src_len], ' ', dst_size - src_len);
+      memset (&dst[src_len], pad, dst_size - src_len);
     }
 }
 
 /* Copies string SRC to buffer DST, of size DST_SIZE bytes.
    DST is truncated to DST_SIZE bytes or padded on the left with
-   spaces as needed. */
+   copies of PAD as needed. */
 void
-buf_copy_str_lpad (char *dst, size_t dst_size, const char *src)
+buf_copy_str_lpad (char *dst, size_t dst_size, const char *src, char pad)
 {
   size_t src_len = strlen (src);
   if (src_len >= dst_size)
@@ -150,40 +153,42 @@ buf_copy_str_lpad (char *dst, size_t dst_size, const char *src)
   else
     {
       size_t pad_cnt = dst_size - src_len;
-      memset (&dst[0], ' ', pad_cnt);
+      memset (&dst[0], pad, pad_cnt);
       memcpy (dst + pad_cnt, src, src_len);
     }
 }
 
 /* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes.
    DST is truncated to DST_SIZE bytes or padded on the left with
-   spaces as needed. */
+   copies of PAD as needed. */
 void
 buf_copy_lpad (char *dst, size_t dst_size,
-               const char *src, size_t src_size)
+               const char *src, size_t src_size,
+               char pad)
 {
   if (src_size >= dst_size)
     memmove (dst, src, dst_size);
   else
     {
-      memset (dst, ' ', dst_size - src_size);
+      memset (dst, pad, dst_size - src_size);
       memmove (&dst[dst_size - src_size], src, src_size);
     }
 }
 
 /* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes.
    DST is truncated to DST_SIZE bytes or padded on the right with
-   spaces as needed. */
+   copies of PAD as needed. */
 void
 buf_copy_rpad (char *dst, size_t dst_size,
-               const char *src, size_t src_size)
+               const char *src, size_t src_size,
+               char pad)
 {
   if (src_size >= dst_size)
     memmove (dst, src, dst_size);
   else
     {
       memmove (dst, src, src_size);
-      memset (&dst[src_size], ' ', dst_size - src_size);
+      memset (&dst[src_size], pad, dst_size - src_size);
     }
 }
 
@@ -194,15 +199,18 @@ buf_copy_rpad (char *dst, size_t dst_size,
 void
 str_copy_rpad (char *dst, size_t dst_size, const char *src)
 {
-  size_t src_len = strlen (src);
-  if (src_len < dst_size - 1)
+  if (dst_size > 0) 
     {
-      memcpy (dst, src, src_len);
-      memset (&dst[src_len], ' ', dst_size - 1 - src_len);
+      size_t src_len = strlen (src);
+      if (src_len < dst_size - 1)
+        {
+          memcpy (dst, src, src_len);
+          memset (&dst[src_len], ' ', dst_size - 1 - src_len);
+        }
+      else
+        memcpy (dst, src, dst_size - 1);
+      dst[dst_size - 1] = 0;
     }
-  else
-    memcpy (dst, src, dst_size - 1);
-  dst[dst_size - 1] = 0;
 }
 
 /* Copies SRC to DST, which is in a buffer DST_SIZE bytes long.
@@ -372,14 +380,13 @@ ss_tail (struct substring ss, size_t cnt)
     return ss;
 }
 
-/* Makes a malloc()'d copy of the contents of OLD
+/* Makes a malloc()'d, null-terminated copy of the contents of OLD
    and stores it in NEW. */
 void
 ss_alloc_substring (struct substring *new, struct substring old)
 {
-  new->string = xmalloc (old.length);
+  new->string = xmemdup0 (old.string, old.length);
   new->length = old.length;
-  memcpy (new->string, old.string, old.length);
 }
 
 /* Allocates room for a CNT-character string in NEW. */
@@ -546,6 +553,23 @@ ss_match_char (struct substring *ss, char c)
     return false;
 }
 
+/* If the first character in SS is in MATCH, removes it and
+   returns the character that was removed.
+   Otherwise, returns EOF without changing the string. */
+int
+ss_match_char_in (struct substring *ss, struct substring match)
+{
+  int c = EOF;
+  if (ss->length > 0
+      && memchr (match.string, ss->string[0], match.length) != NULL)
+    {
+      c = ss->string[0];
+      ss->string++;
+      ss->length--;
+    }
+  return c;
+}
+
 /* If SS begins with TARGET, removes it and returns true.
    Otherwise, returns false without changing SS. */
 bool
@@ -1048,6 +1072,34 @@ ds_set_length (struct string *st, size_t new_length, char pad)
     st->ss.length = new_length;
 }
 
+/* Removes N characters from ST starting at offset START. */
+void
+ds_remove (struct string *st, size_t start, size_t n)
+{
+  if (n > 0 && start < st->ss.length)
+    {
+      if (st->ss.length - start <= n)
+        {
+          /* All characters at or beyond START are deleted. */
+          st->ss.length = start;
+        }
+      else
+        {
+          /* Some characters remain and must be shifted into
+             position. */
+          memmove (st->ss.string + st->ss.length,
+                   st->ss.string + st->ss.length + n,
+                   st->ss.length - start - n);
+          st->ss.length -= n;
+        }
+    }
+  else
+    {
+      /* There are no characters to delete or no characters at or
+         beyond START, hence deletion is a no-op. */
+    }
+}
+
 /* Returns true if ST is empty, false otherwise. */
 bool
 ds_is_empty (const struct string *st)
@@ -1163,55 +1215,75 @@ ds_capacity (const struct string *st)
 char *
 ds_cstr (const struct string *st_)
 {
-  struct string *st = (struct string *) st_;
+  struct string *st = CONST_CAST (struct string *, st_);
   if (st->ss.string == NULL)
     ds_extend (st, 1);
   st->ss.string[st->ss.length] = '\0';
   return st->ss.string;
 }
 
-/* Appends to ST a newline-terminated line read from STREAM, but
-   no more than MAX_LENGTH characters.
-   Newline is the last character of ST on return, if encountering
-   a newline was the reason for terminating.
-   Returns true if at least one character was read from STREAM
-   and appended to ST, false if no characters at all were read
-   before an I/O error or end of file was encountered (or
-   MAX_LENGTH was 0). */
+/* Returns the value of ST as a null-terminated string and then
+   reinitialized ST as an empty string.  The caller must free the
+   returned string with free(). */
+char *
+ds_steal_cstr (struct string *st)
+{
+  char *s = ds_cstr (st);
+  ds_init_empty (st);
+  return s;
+}
+
+/* Reads characters from STREAM and appends them to ST, stopping
+   after MAX_LENGTH characters, after appending a newline, or
+   after an I/O error or end of file was encountered, whichever
+   comes first.  Returns true if at least one character was added
+   to ST, false if no characters were read before an I/O error or
+   end of file (or if MAX_LENGTH was 0).
+
+   This function treats LF and CR LF sequences as new-line,
+   translating each of them to a single '\n' new-line character
+   in ST. */
 bool
 ds_read_line (struct string *st, FILE *stream, size_t max_length)
 {
-  if (!st->ss.length && max_length == SIZE_MAX)
-    {
-      size_t capacity = st->capacity ? st->capacity + 1 : 0;
-      ssize_t n = getline (&st->ss.string, &capacity, stream);
-      if (capacity)
-        st->capacity = capacity - 1;
-      if (n > 0)
-        {
-          st->ss.length = n;
-          return true;
-        }
-      else
-        return false;
-    }
-  else
-    {
-      size_t length;
+  size_t length;
 
-      for (length = 0; length < max_length; length++)
+  for (length = 0; length < max_length; length++)
+    {
+      int c = getc (stream);
+      switch (c)
         {
-          int c = getc (stream);
-          if (c == EOF)
-            break;
+        case EOF:
+          return length > 0;
 
+        case '\n':
           ds_put_char (st, c);
+          return true;
+
+        case '\r':
+          c = getc (stream);
           if (c == '\n')
-            return true;
+            {
+              /* CR followed by LF is special: translate to \n. */
+              ds_put_char (st, '\n');
+              return true;
+            }
+          else
+            {
+              /* CR followed by anything else is just CR. */
+              ds_put_char (st, '\r');
+              if (c == EOF)
+                return true;
+              ungetc (c, stream);
+            }
+          break;
+
+        default:
+          ds_put_char (st, c);
         }
-
-      return length > 0;
     }
+
+  return length > 0;
 }
 
 /* Removes a comment introduced by `#' from ST,
@@ -1379,3 +1451,44 @@ ds_put_char_multiple (struct string *st, int ch, size_t cnt)
 {
   memset (ds_put_uninit (st, cnt), ch, cnt);
 }
+
+
+/* If relocation has been enabled, replace ST,
+   with its relocated version */
+void
+ds_relocate (struct string *st)
+{
+  const char *orig = ds_cstr (st);
+  const char *rel = relocate (orig);
+
+  if ( orig != rel)
+    {
+      ds_clear (st);
+      ds_put_cstr (st, rel);
+      /* The documentation for relocate says that casting away const
+       and then freeing is appropriate ... */
+      free (CONST_CAST (char *, rel));
+    }
+}
+
+
+\f
+
+/* Operations on uint8_t "strings" */
+
+/* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes.
+   DST is truncated to DST_SIZE bytes or padded on the right with
+   copies of PAD as needed. */
+void
+u8_buf_copy_rpad (uint8_t *dst, size_t dst_size,
+                 const uint8_t *src, size_t src_size,
+                 char pad)
+{
+  if (src_size >= dst_size)
+    memmove (dst, src, dst_size);
+  else
+    {
+      memmove (dst, src, src_size);
+      memset (&dst[src_size], pad, dst_size - src_size);
+    }
+}