summaryrefslogtreecommitdiff
path: root/src/pstring.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pstring.c')
-rw-r--r--src/pstring.c310
1 files changed, 310 insertions, 0 deletions
diff --git a/src/pstring.c b/src/pstring.c
new file mode 100644
index 0000000..ef845f3
--- /dev/null
+++ b/src/pstring.c
@@ -0,0 +1,310 @@
+/*--------------------------------------------------------------------
+ * Copyright © 2012-2016 James Hunt <jamesodhunt@ubuntu.com>.
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *--------------------------------------------------------------------
+ */
+
+#include "pstring.h"
+
+#include <assert.h>
+
+extern wchar_t wide_indent_char;
+
+wchar_t *
+char_to_wchar (const char *str)
+{
+ const char *p;
+ wchar_t *wstr = NULL;
+ size_t len;
+ size_t bytes;
+
+ assert (str);
+
+ len = mbsrtowcs (NULL, &str, 0, NULL);
+ if (len <= 0)
+ return NULL;
+
+ /* include space for terminator */
+ bytes = (1 + len) * sizeof (wchar_t);
+
+ wstr = malloc (bytes);
+ if (! wstr)
+ return NULL;
+
+ p = str;
+
+ if (mbsrtowcs (wstr, &p, len, NULL) != len)
+ goto error;
+
+ /* ensure it's terminated */
+ wstr[len] = L'\0';
+
+ return wstr;
+
+error:
+ free (wstr);
+ return NULL;
+}
+
+char *
+wchar_to_char (const wchar_t *wstr)
+{
+ char *str = NULL;
+ size_t len;
+ size_t bytes;
+ size_t ret;
+
+ assert (wstr);
+
+ len = wcslen (wstr);
+
+ /* determine number of MBS (char) bytes requires to hold the
+ * wchar_t string.
+ */
+ bytes = wcstombs (NULL, wstr, len);
+ if (! bytes)
+ return NULL;
+
+ str = calloc (bytes + 1, sizeof (char));
+ if (! str)
+ return NULL;
+
+ /* actually perform the conversion */
+ ret = wcstombs (str, wstr, bytes);
+
+ if (! ret)
+ goto error;
+
+ return str;
+
+error:
+ free (str);
+ return NULL;
+}
+
+pstring *
+pstring_new (void)
+{
+ pstring *pstr = NULL;
+
+ pstr = calloc (1, sizeof (pstring));
+ if (! pstr)
+ return NULL;
+
+ pstr->len = 0;
+ pstr->size = 0;
+ pstr->buf = NULL;
+
+ return pstr;
+}
+
+pstring *
+pstring_create (const wchar_t *str)
+{
+ pstring *pstr = NULL;
+
+ assert (str);
+
+ pstr = pstring_new ();
+
+ if (! pstr)
+ return NULL;
+
+ pstr->buf = wcsdup (str);
+ if (! pstr->buf) {
+ pstring_free (pstr);
+ return NULL;
+ }
+
+ /* include the L'\0' terminator */
+ pstr->len = 1 + wcslen (pstr->buf);
+
+ pstr->size = pstr->len * sizeof (wchar_t);
+
+ return pstr;
+}
+
+void
+pstring_free (pstring *str)
+{
+ assert (str);
+
+ if (str->buf)
+ free (str->buf);
+
+ free (str);
+}
+
+pstring *
+char_to_pstring (const char *str)
+{
+ pstring *pstr = NULL;
+ wchar_t *s;
+
+ assert (str);
+
+ s = char_to_wchar (str);
+ if (! s)
+ return NULL;
+
+ pstr = pstring_create (s);
+
+ free (s);
+
+ return pstr;
+}
+
+char *
+pstring_to_char (const pstring *str)
+{
+ assert (str);
+
+ return wchar_to_char (str->buf);
+}
+
+/**
+ * pstring_chomp:
+ *
+ * Remove trailing extraneous newlines and indent_chars from @str.
+ **/
+void
+pstring_chomp (pstring *str)
+{
+ size_t len;
+ int removable = 0;
+ wchar_t *p;
+
+ assert (str);
+
+ /* Unable to add '\n' in this scenario */
+ if (str->len < 2)
+ return;
+
+ for (p = str->buf+str->len-1;
+ *p == L'\n' || *p == wide_indent_char;
+ p--, removable++)
+ ;
+
+ /* Chop string at the appropriate place after first adding a new
+ * newline.
+ */
+ if (removable > 1) {
+ len = str->len - (removable-1);
+ str->buf[len-1] = L'\n';
+ str->buf[len] = L'\0';
+ str->len = len;
+ }
+}
+
+/**
+ * pstring_compress:
+ *
+ * Remove lines composed entirely of whitespace from @str.
+ *
+ * This is required specifically for '--output=text' which in some
+ * scenarios generates lines comprising pure whitespace. This is
+ * unecessary and results from the fact that when an
+ * ELEMENT_TYPE_OBJECT_* is encountered, formatting is applied for the
+ * previously seen element, but sometimes such "objects" should be
+ * invisible.
+ **/
+void
+pstring_compress (pstring **wstr, wchar_t remove_char)
+{
+ wchar_t *from;
+ wchar_t *to;
+ wchar_t *p;
+ wchar_t *start;
+ size_t count = 0;
+ size_t blanks = 0;
+ size_t new_len;
+ size_t bytes;
+
+ assert (wstr);
+
+ to = from = (*wstr)->buf;
+ assert (from);
+
+ while (to && *to) {
+again:
+ while (*to == L'\n' && *(to+1) == L'\n') {
+ /* skip over blank lines */
+ to++;
+ blanks++;
+ }
+
+ start = to;
+
+ while (*to == remove_char) {
+ /* skip runs of contiguous characters */
+ to++;
+ count++;
+ }
+
+ if (to != start) {
+ /* Only start consuming NLs at the end of a
+ * contiguous run *iff* there was more than a
+ * single removed char. This is a heuristic to
+ * avoid removing valid entries for example env
+ * vars that are set to nul are shown as:
+ *
+ * 'var: '
+ *
+ * Shudder.
+ */
+ if (*to == L'\n' && to != start+1) {
+ while (*to == L'\n') {
+ /* consume the NL at the end of the contiguous run */
+ to++;
+ }
+
+ /* check to ensure that we haven't entered a new line
+ * containing another block of chars to remove.
+ */
+ if (*to == remove_char)
+ goto again;
+
+ blanks++;
+
+ } else {
+ /* not a full line so backtrack */
+ to = start;
+ count = 0;
+ }
+ }
+
+ *from++ = *to++;
+ }
+
+ /* terminate */
+ *from = L'\0';
+
+ if (blanks || count) {
+ new_len = (*wstr)->len - (blanks + count);
+
+ bytes = new_len * sizeof (wchar_t);
+
+ p = realloc ((*wstr)->buf, bytes);
+ assert (p);
+
+ (*wstr)->buf = p;
+
+ (*wstr)->buf[new_len-1] = L'\0';
+
+ (*wstr)->len = new_len;
+ (*wstr)->size = bytes;
+ }
+}
+