/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2014 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 "libpspp/message.h"
#include "libpspp/pool.h"
+#include "gl/c-ctype.h"
+#include "gl/c-vasnprintf.h"
#include "gl/relocatable.h"
#include "gl/minmax.h"
#include "gl/xalloc.h"
void
str_copy_rpad (char *dst, size_t dst_size, const char *src)
{
- if (dst_size > 0)
+ if (dst_size > 0)
{
size_t src_len = strlen (src);
if (src_len < dst_size - 1)
dst[dst_len] = '\0';
}
-/* Converts each byte in S to uppercase. */
+/* Converts each byte in S to uppercase.
+
+ This is suitable only for ASCII strings. Use utf8_to_upper() for UTF-8
+ strings.*/
void
str_uppercase (char *s)
{
for (; *s != '\0'; s++)
- *s = toupper ((unsigned char) *s);
+ *s = c_toupper ((unsigned char) *s);
}
-/* Converts each byte in S to lowercase. */
+/* Converts each byte in S to lowercase.
+
+ This is suitable only for ASCII strings. Use utf8_to_lower() for UTF-8
+ strings.*/
void
str_lowercase (char *s)
{
for (; *s != '\0'; s++)
- *s = tolower ((unsigned char) *s);
+ *s = c_tolower ((unsigned char) *s);
}
/* Converts NUMBER into a string in 26-adic notation in BUFFER,
- which has room for SIZE bytes. Returns true if successful,
- false if NUMBER, plus a trailing null, is too large to fit in
- the available space.
+ which has room for SIZE bytes. Uses uppercase if UPPERCASE is
+ true, otherwise lowercase, Returns true if successful, false
+ if NUMBER, plus a trailing null, is too large to fit in the
+ available space.
26-adic notation is "spreadsheet column numbering": 1 = A, 2 =
B, 3 = C, ... 26 = Z, 27 = AA, 28 = AB, 29 = AC, ...
For more information, see
http://en.wikipedia.org/wiki/Bijective_numeration. */
bool
-str_format_26adic (unsigned long int number, char buffer[], size_t size)
+str_format_26adic (unsigned long int number, bool uppercase,
+ char buffer[], size_t size)
{
+ const char *alphabet
+ = uppercase ? "ABCDEFGHIJKLMNOPQRSTUVWXYZ" : "abcdefghijklmnopqrstuvwxyz";
size_t length = 0;
while (number-- > 0)
{
if (length >= size)
- return false;
- buffer[length++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[number % 26];
+ goto overflow;
+ buffer[length++] = alphabet[number % 26];
number /= 26;
}
if (length >= size)
- return false;
+ goto overflow;
buffer[length] = '\0';
buf_reverse (buffer, length);
return true;
+
+overflow:
+ if (length > 0)
+ buffer[0] = '\0';
+ return false;
}
/* Sets the SIZE bytes starting at BLOCK to C,
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. */
+/* Makes a pool_alloc_unaligned()'d, null-terminated copy of the contents of
+ OLD in POOL, and stores it in NEW. */
void
ss_alloc_substring_pool (struct substring *new, struct substring old,
struct pool *pool)
{
- new->string = pool_alloc_unaligned (pool, old.length);
+ new->string = pool_alloc_unaligned (pool, old.length + 1);
new->length = old.length;
memcpy (new->string, old.string, old.length);
+ new->string[old.length] = '\0';
}
/* Allocates room for a CNT-byte string in NEW in POOL. */
ss_tokenize (struct substring ss, struct substring delimiters,
size_t *save_idx, struct substring *token)
{
+ bool found_token;
+
ss_advance (&ss, *save_idx);
*save_idx += ss_ltrim (&ss, delimiters);
ss_get_bytes (&ss, ss_cspan (ss, delimiters), token);
- *save_idx += ss_length (*token) + 1;
- return ss_length (*token) > 0;
+
+ found_token = ss_length (*token) > 0;
+ *save_idx += ss_length (*token) + found_token;
+ return found_token;
}
/* Removes the first CNT bytes from SS. */
return end;
}
+/* Moves the bytes in ST following offset OFS + OLD_LEN in ST to offset OFS +
+ NEW_LEN and returns the byte at offset OFS. The first min(OLD_LEN, NEW_LEN)
+ bytes at the returned position are unchanged; if NEW_LEN > OLD_LEN then the
+ following NEW_LEN - OLD_LEN bytes are initially indeterminate.
+
+ The intention is that the caller should write NEW_LEN bytes at the returned
+ position, to effectively replace the OLD_LEN bytes previously at that
+ position. */
+char *
+ds_splice_uninit (struct string *st,
+ size_t ofs, size_t old_len, size_t new_len)
+{
+ if (new_len != old_len)
+ {
+ if (new_len > old_len)
+ ds_extend (st, ds_length (st) + (new_len - old_len));
+ memmove (ds_data (st) + (ofs + new_len),
+ ds_data (st) + (ofs + old_len),
+ ds_length (st) - (ofs + old_len));
+ st->ss.length += new_len - old_len;
+ }
+ return ds_data (st) + ofs;
+}
+
/* Formats FORMAT as a printf string and appends the result to ST. */
void
ds_put_format (struct string *st, const char *format, ...)
va_end (args);
}
-/* Formats FORMAT as a printf string and appends the result to ST. */
+/* Formats FORMAT as a printf string as if in the C locale and appends the result to ST. */
void
-ds_put_vformat (struct string *st, const char *format, va_list args_)
+ds_put_c_format (struct string *st, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ ds_put_c_vformat (st, format, args);
+ va_end (args);
+}
+
+
+/* Formats FORMAT as a printf string, using fmt_func (a snprintf like function)
+ and appends the result to ST. */
+static void
+ds_put_vformat_int (struct string *st, const char *format, va_list args_,
+ int (*fmt_func) (char *, size_t, const char *, va_list))
{
int avail, needed;
va_list args;
va_copy (args, args_);
avail = st->ss.string != NULL ? st->capacity - st->ss.length + 1 : 0;
- needed = vsnprintf (st->ss.string + st->ss.length, avail, format, args);
+ needed = fmt_func (st->ss.string + st->ss.length, avail, format, args);
va_end (args);
if (needed >= avail)
{
va_copy (args, args_);
- vsprintf (ds_put_uninit (st, needed), format, args);
+ fmt_func (ds_put_uninit (st, needed), needed + 1, format, args);
va_end (args);
}
else
avail = st->capacity - st->ss.length + 1;
va_copy (args, args_);
- needed = vsnprintf (ds_end (st), avail, format, args);
+ needed = fmt_func (ds_end (st), avail, format, args);
va_end (args);
}
st->ss.length += needed;
}
}
+
+static int
+vasnwrapper (char *str, size_t size, const char *format, va_list ap)
+{
+ c_vasnprintf (str, &size, format, ap);
+ return size;
+}
+
+/* Formats FORMAT as a printf string and appends the result to ST. */
+void
+ds_put_vformat (struct string *st, const char *format, va_list args_)
+{
+ ds_put_vformat_int (st, format, args_, vsnprintf);
+}
+
+/* Formats FORMAT as a printf string, as if in the C locale,
+ and appends the result to ST. */
+void
+ds_put_c_vformat (struct string *st, const char *format, va_list args_)
+{
+ ds_put_vformat_int (st, format, args_, vasnwrapper);
+}
+
/* Appends byte CH to ST. */
void
ds_put_byte (struct string *st, int ch)
memset (ds_put_uninit (st, cnt), ch, cnt);
}
+/* Appends Unicode code point UC to ST in UTF-8 encoding. */
+void
+ds_put_unichar (struct string *st, ucs4_t uc)
+{
+ ds_extend (st, ds_length (st) + 6);
+ st->ss.length += u8_uctomb (CHAR_CAST (uint8_t *, ds_end (st)), uc, 6);
+}
/* If relocation has been enabled, replace ST,
with its relocated version */