+char **getSplitStringArray(const char *s, const char *separators, int max_tokens)
+{
+ const char *s_ptr, *s_last = s;
+ byte separator_table[256] = { FALSE };
+ int num_tokens;
+ char **tokens = NULL;
+
+ if (s == NULL)
+ return NULL;
+
+ if (separators == NULL)
+ return NULL;
+
+ if (max_tokens < 1)
+ max_tokens = INT_MAX;
+
+ // if string is empty, return empty array
+ if (*s == '\0')
+ {
+ tokens = checked_malloc(sizeof(char *));
+ tokens[0] = NULL;
+
+ return tokens;
+ }
+
+ // initialize separator table for all characters in separators string
+ for (s_ptr = separators; *s_ptr != '\0'; s_ptr++)
+ separator_table[*(byte *)s_ptr] = TRUE;
+
+ // count number of tokens in string
+ for (num_tokens = 1, s_ptr = s; *s_ptr != '\0'; s_ptr++)
+ if (separator_table[*(byte *)s_ptr] && num_tokens < max_tokens)
+ num_tokens++;
+
+ // allocate array for determined number of tokens
+ tokens = checked_malloc((num_tokens + 1) * sizeof(char *));
+
+ // copy all but last separated sub-strings to array
+ for (num_tokens = 0, s_ptr = s; *s_ptr != '\0'; s_ptr++)
+ {
+ if (separator_table[*(byte *)s_ptr] && num_tokens + 1 < max_tokens)
+ {
+ tokens[num_tokens++] = getStringCopyN(s_last, s_ptr - s_last);
+ s_last = s_ptr + 1;
+ }
+ }
+
+ // copy last separated sub-string to array
+ tokens[num_tokens++] = getStringCopyN(s_last, s_ptr - s_last);
+
+ // terminate array
+ tokens[num_tokens] = NULL;
+
+ return tokens;
+}
+
+int getStringArrayLength(char **s_array)
+{
+ int num_strings = 0;
+
+ if (s_array == NULL)
+ return 0;
+
+ while (s_array[num_strings] != NULL)
+ num_strings++;
+
+ return num_strings;
+}
+
+void freeStringArray(char **s_array)
+{
+ int i;
+
+ if (s_array == NULL)
+ return;
+
+ for (i = 0; s_array[i] != NULL; i++)
+ checked_free(s_array[i]);
+
+ checked_free(s_array);
+}
+
+char *getEscapedString(const char *s)
+{
+ const unsigned char *s_ptr = (unsigned char *)s;
+ char *s_escaped;
+ char *s_escaped_ptr;
+
+ if (s == NULL)
+ return NULL;
+
+ /* Each source byte needs maximally four target chars (\777) */
+ s_escaped = checked_malloc(strlen(s) * 4 + 1);
+ s_escaped_ptr = s_escaped;
+
+ while (*s_ptr != '\0')
+ {
+ switch (*s_ptr)
+ {
+ case '\b':
+ *s_escaped_ptr++ = '\\';
+ *s_escaped_ptr++ = 'b';
+ break;
+
+ case '\f':
+ *s_escaped_ptr++ = '\\';
+ *s_escaped_ptr++ = 'f';
+ break;
+
+ case '\n':
+ *s_escaped_ptr++ = '\\';
+ *s_escaped_ptr++ = 'n';
+ break;
+
+ case '\r':
+ *s_escaped_ptr++ = '\\';
+ *s_escaped_ptr++ = 'r';
+ break;
+
+ case '\t':
+ *s_escaped_ptr++ = '\\';
+ *s_escaped_ptr++ = 't';
+ break;
+
+ case '\v':
+ *s_escaped_ptr++ = '\\';
+ *s_escaped_ptr++ = 'v';
+ break;
+
+ case '\\':
+ *s_escaped_ptr++ = '\\';
+ *s_escaped_ptr++ = '\\';
+ break;
+
+ case '"':
+ *s_escaped_ptr++ = '\\';
+ *s_escaped_ptr++ = '"';
+ break;
+
+ default:
+ if ((*s_ptr < ' ') || (*s_ptr >= 0177))
+ {
+ *s_escaped_ptr++ = '\\';
+ *s_escaped_ptr++ = '0' + (((*s_ptr) >> 6) & 07);
+ *s_escaped_ptr++ = '0' + (((*s_ptr) >> 3) & 07);
+ *s_escaped_ptr++ = '0' + ( (*s_ptr) & 07);
+ }
+ else
+ {
+ *s_escaped_ptr++ = *s_ptr;
+ }
+ break;
+ }
+
+ s_ptr++;
+ }
+
+ *s_escaped_ptr = '\0';
+
+ return s_escaped;
+}
+
+char *getUnescapedString(const char *s)
+{
+ const char *s_ptr = s;
+ const char *octal_ptr;
+ char *s_unescaped;
+ char *s_unescaped_ptr;
+
+ if (s == NULL)
+ return NULL;
+
+ s_unescaped = checked_malloc(strlen(s) + 1);
+ s_unescaped_ptr = s_unescaped;
+
+ while (*s_ptr != '\0')
+ {
+ if (*s_ptr == '\\')
+ {
+ s_ptr++;
+
+ switch (*s_ptr)
+ {
+ case '\0':
+ Warn("getUnescapedString: trailing \\");
+ goto out;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ *s_unescaped_ptr = 0;
+ octal_ptr = s_ptr;
+
+ while (s_ptr < octal_ptr + 3 && *s_ptr >= '0' && *s_ptr <= '7')
+ {
+ *s_unescaped_ptr = (*s_unescaped_ptr * 8) + (*s_ptr - '0');
+ s_ptr++;
+ }
+
+ s_unescaped_ptr++;
+ s_ptr--;
+ break;
+
+ case 'b':
+ *s_unescaped_ptr++ = '\b';
+ break;
+
+ case 'f':
+ *s_unescaped_ptr++ = '\f';
+ break;
+
+ case 'n':
+ *s_unescaped_ptr++ = '\n';
+ break;
+
+ case 'r':
+ *s_unescaped_ptr++ = '\r';
+ break;
+
+ case 't':
+ *s_unescaped_ptr++ = '\t';
+ break;
+
+ case 'v':
+ *s_unescaped_ptr++ = '\v';
+ break;
+
+ default:
+ /* also handles \" and \\ */
+ *s_unescaped_ptr++ = *s_ptr;
+ break;
+ }
+ }
+ else
+ {
+ *s_unescaped_ptr++ = *s_ptr;
+ }
+
+ s_ptr++;
+ }
+
+ out:
+ *s_unescaped_ptr = '\0';
+
+ return s_unescaped;
+}
+
+char *chugString(char *s)
+{
+ if (s == NULL)
+ return NULL;
+
+ char *start;
+
+ for (start = (char *)s; *start && isspace(*start); start++)
+ ;
+
+ memmove(s, start, strlen(start) + 1);
+
+ return s;
+}
+
+char *chompString(char *s)
+{
+ if (s == NULL)
+ return NULL;
+
+ int len = strlen(s);
+
+ while (len--)
+ {
+ if (isspace(s[len]))
+ s[len] = '\0';
+ else
+ break;
+ }
+
+ return s;
+}
+
+char *stripString(char *s)
+{
+ return chugString(chompString(s));
+}
+
+boolean strEqual(const char *s1, const char *s2)