str: New functions for checking for and removing string suffixes.
[pspp-builds.git] / src / libpspp / str.c
index cd2363a0026c88454a26fbca0145603692bb7329..ba4a26f1426ac51552dd31885d5cdfff5fea321d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011 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
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <unistr.h>
 
 #include "libpspp/cast.h"
 #include "libpspp/message.h"
@@ -340,6 +341,12 @@ ss_alloc_uninit (struct substring *new, size_t cnt)
   new->length = cnt;
 }
 
+void
+ss_realloc (struct substring *ss, size_t size)
+{
+  ss->string = xrealloc (ss->string, size);
+}
+
 /* Makes a pool_alloc_unaligned()'d copy of the contents of OLD
    in POOL, and stores it in NEW. */
 void
@@ -409,7 +416,7 @@ ss_trim (struct substring *ss, struct substring trim_set)
 /* If the last byte in SS is C, removes it and returns true.
    Otherwise, returns false without changing the string. */
 bool
-ss_chomp (struct substring *ss, char c)
+ss_chomp_byte (struct substring *ss, char c)
 {
   if (ss_last (*ss) == c)
     {
@@ -420,6 +427,20 @@ ss_chomp (struct substring *ss, char c)
     return false;
 }
 
+/* If SS ends with SUFFIX, removes it and returns true.
+   Otherwise, returns false without changing the string. */
+bool
+ss_chomp (struct substring *ss, struct substring suffix)
+{
+  if (ss_ends_with (*ss, suffix))
+    {
+      ss->length -= suffix.length;
+      return true;
+    }
+  else
+    return false;
+}
+
 /* Divides SS into tokens separated by any of the DELIMITERS.
    Each call replaces TOKEN by the next token in SS, or by an
    empty string if no tokens remain.  Returns true if a token was
@@ -648,6 +669,15 @@ ss_last (struct substring ss)
   return ss.length > 0 ? (unsigned char) ss.string[ss.length - 1] : EOF;
 }
 
+/* Returns true if SS ends with SUFFIX, false otherwise. */
+bool
+ss_ends_with (struct substring ss, struct substring suffix)
+{
+  return (ss.length >= suffix.length
+          && !memcmp (&ss.string[ss.length - suffix.length], suffix.string,
+                      suffix.length));
+}
+
 /* Returns the number of contiguous bytes at the beginning
    of SS that are in SKIP_SET. */
 size_t
@@ -740,6 +770,81 @@ ss_xstrdup (struct substring ss)
   s[ss.length] = '\0';
   return s;
 }
+/* UTF-8. */
+
+/* Returns the character represented by the UTF-8 sequence at the start of S.
+   The return value is either a Unicode code point in the range 0 to 0x10ffff,
+   or UINT32_MAX if S is empty. */
+ucs4_t
+ss_first_mb (struct substring s)
+{
+  return ss_at_mb (s, 0);
+}
+
+/* Returns the number of bytes in the UTF-8 character at the beginning of S.
+
+   The return value is 0 if S is empty, otherwise between 1 and 4. */
+int
+ss_first_mblen (struct substring s)
+{
+  return ss_at_mblen (s, 0);
+}
+
+/* Advances S past the UTF-8 character at its beginning.  Returns the Unicode
+   code point that was skipped (in the range 0 to 0x10ffff), or UINT32_MAX if S
+   was not modified because it was initially empty. */
+ucs4_t
+ss_get_mb (struct substring *s)
+{
+  if (s->length > 0)
+    {
+      ucs4_t uc;
+      int n;
+
+      n = u8_mbtouc (&uc, CHAR_CAST (const uint8_t *, s->string), s->length);
+      s->string += n;
+      s->length -= n;
+      return uc;
+    }
+  else
+    return UINT32_MAX;
+}
+
+/* Returns the character represented by the UTF-8 sequence starting OFS bytes
+   into S.  The return value is either a Unicode code point in the range 0 to
+   0x10ffff, or UINT32_MAX if OFS is past the last byte in S.
+
+   (Returns 0xfffd if OFS points into the middle, not the beginning, of a UTF-8
+   sequence.)  */
+ucs4_t
+ss_at_mb (struct substring s, size_t ofs)
+{
+  if (s.length > ofs)
+    {
+      ucs4_t uc;
+      u8_mbtouc (&uc, CHAR_CAST (const uint8_t *, s.string + ofs),
+                 s.length - ofs);
+      return uc;
+    }
+  else
+    return UINT32_MAX;
+}
+
+/* Returns the number of bytes represented by the UTF-8 sequence starting OFS
+   bytes into S.  The return value is 0 if OFS is past the last byte in S,
+   otherwise between 1 and 4. */
+int
+ss_at_mblen (struct substring s, size_t ofs)
+{
+  if (s.length > ofs)
+    {
+      ucs4_t uc;
+      return u8_mbtouc (&uc, CHAR_CAST (const uint8_t *, s.string + ofs),
+                        s.length - ofs);
+    }
+  else
+    return 0;
+}
 \f
 /* Initializes ST as an empty string. */
 void
@@ -954,9 +1059,17 @@ ds_trim (struct string *st, struct substring trim_set)
 /* If the last byte in ST is C, removes it and returns true.
    Otherwise, returns false without modifying ST. */
 bool
-ds_chomp (struct string *st, char c)
+ds_chomp_byte (struct string *st, char c)
+{
+  return ss_chomp_byte (&st->ss, c);
+}
+
+/* If ST ends with SUFFIX, removes it and returns true.
+   Otherwise, returns false without modifying ST. */
+bool
+ds_chomp (struct string *st, struct substring suffix)
 {
-  return ss_chomp (&st->ss, c);
+  return ss_chomp (&st->ss, suffix);
 }
 
 /* Divides ST into tokens separated by any of the DELIMITERS.
@@ -1098,6 +1211,13 @@ ds_last (const struct string *st)
   return ss_last (ds_ss (st));
 }
 
+/* Returns true if ST ends with SUFFIX, false otherwise. */
+bool
+ds_ends_with (const struct string *st, struct substring suffix)
+{
+  return ss_ends_with (st->ss, suffix);
+}
+
 /* Returns the number of consecutive bytes at the beginning
    of ST that are in SKIP_SET. */
 size_t
@@ -1276,7 +1396,7 @@ ds_read_config_line (struct string *st, int *line_number, FILE *stream)
       (*line_number)++;
       ds_rtrim (st, ss_cstr (CC_SPACES));
     }
-  while (ds_chomp (st, '\\'));
+  while (ds_chomp_byte (st, '\\'));
 
   remove_comment (st);
   return true;