+/*
+ * Find a character in a string, unless it's a colon contained within
+ * square brackets. Used for untangling strings of the form
+ * 'host:port', where host can be an IPv6 literal.
+ *
+ * We provide several variants of this function, with semantics like
+ * various standard string.h functions.
+ */
+static const char *host_strchr_internal(const char *s, const char *set,
+ int first)
+{
+ int brackets = 0;
+ const char *ret = NULL;
+
+ while (1) {
+ if (!*s)
+ return ret;
+
+ if (*s == '[')
+ brackets++;
+ else if (*s == ']' && brackets > 0)
+ brackets--;
+ else if (brackets && *s == ':')
+ /* never match */ ;
+ else if (strchr(set, *s)) {
+ ret = s;
+ if (first)
+ return ret;
+ }
+
+ s++;
+ }
+}
+size_t host_strcspn(const char *s, const char *set)
+{
+ const char *answer = host_strchr_internal(s, set, TRUE);
+ if (answer)
+ return answer - s;
+ else
+ return strlen(s);
+}
+char *host_strchr(const char *s, int c)
+{
+ char set[2];
+ set[0] = c;
+ set[1] = '\0';
+ return (char *) host_strchr_internal(s, set, TRUE);
+}
+char *host_strrchr(const char *s, int c)
+{
+ char set[2];
+ set[0] = c;
+ set[1] = '\0';
+ return (char *) host_strchr_internal(s, set, FALSE);
+}
+
+#ifdef TEST_HOST_STRFOO
+int main(void)
+{
+ int passes = 0, fails = 0;
+
+#define TEST1(func, string, arg2, suffix, result) do \
+ { \
+ const char *str = string; \
+ unsigned ret = func(string, arg2) suffix; \
+ if (ret == result) { \
+ passes++; \
+ } else { \
+ printf("fail: %s(%s,%s)%s = %u, expected %u\n", \
+ #func, #string, #arg2, #suffix, ret, result); \
+ fails++; \
+ } \
+} while (0)
+
+ TEST1(host_strchr, "[1:2:3]:4:5", ':', -str, 7);
+ TEST1(host_strrchr, "[1:2:3]:4:5", ':', -str, 9);
+ TEST1(host_strcspn, "[1:2:3]:4:5", "/:",, 7);
+ TEST1(host_strchr, "[1:2:3]", ':', == NULL, 1);
+ TEST1(host_strrchr, "[1:2:3]", ':', == NULL, 1);
+ TEST1(host_strcspn, "[1:2:3]", "/:",, 7);
+ TEST1(host_strcspn, "[1:2/3]", "/:",, 4);
+ TEST1(host_strcspn, "[1:2:3]/", "/:",, 7);
+
+ printf("passed %d failed %d total %d\n", passes, fails, passes+fails);
+ return fails != 0 ? 1 : 0;
+}
+/* Stubs to stop the rest of this module causing compile failures. */
+void modalfatalbox(const char *fmt, ...) {}
+int conf_get_int(Conf *conf, int primary) { return 0; }
+char *conf_get_str(Conf *conf, int primary) { return NULL; }
+#endif /* TEST_HOST_STRFOO */
+
+/*
+ * Trim square brackets off the outside of an IPv6 address literal.
+ * Leave all other strings unchanged. Returns a fresh dynamically
+ * allocated string.
+ */
+char *host_strduptrim(const char *s)
+{
+ if (s[0] == '[') {
+ const char *p = s+1;
+ int colons = 0;
+ while (*p && *p != ']') {
+ if (isxdigit((unsigned char)*p))
+ /* OK */;
+ else if (*p == ':')
+ colons++;
+ else
+ break;
+ p++;
+ }
+ if (*p == ']' && !p[1] && colons > 1) {
+ /*
+ * This looks like an IPv6 address literal (hex digits and
+ * at least two colons, contained in square brackets).
+ * Trim off the brackets.
+ */
+ return dupprintf("%.*s", (int)(p - (s+1)), s+1);
+ }
+ }
+
+ /*
+ * Any other shape of string is simply duplicated.
+ */
+ return dupstr(s);
+}
+
+prompts_t *new_prompts(void *frontend)
+{
+ prompts_t *p = snew(prompts_t);
+ p->prompts = NULL;
+ p->n_prompts = 0;
+ p->frontend = frontend;
+ p->data = NULL;
+ p->to_server = TRUE; /* to be on the safe side */
+ p->name = p->instruction = NULL;
+ p->name_reqd = p->instr_reqd = FALSE;
+ return p;
+}
+void add_prompt(prompts_t *p, char *promptstr, int echo)
+{
+ prompt_t *pr = snew(prompt_t);
+ pr->prompt = promptstr;
+ pr->echo = echo;
+ pr->result = NULL;
+ pr->resultsize = 0;
+ p->n_prompts++;
+ p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *);
+ p->prompts[p->n_prompts-1] = pr;
+}
+void prompt_ensure_result_size(prompt_t *pr, int newlen)
+{
+ if ((int)pr->resultsize < newlen) {
+ char *newbuf;
+ newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */
+
+ /*
+ * We don't use sresize / realloc here, because we will be
+ * storing sensitive stuff like passwords in here, and we want
+ * to make sure that the data doesn't get copied around in
+ * memory without the old copy being destroyed.
+ */
+ newbuf = snewn(newlen, char);
+ memcpy(newbuf, pr->result, pr->resultsize);
+ smemclr(pr->result, pr->resultsize);
+ sfree(pr->result);
+ pr->result = newbuf;
+ pr->resultsize = newlen;
+ }
+}
+void prompt_set_result(prompt_t *pr, const char *newstr)
+{
+ prompt_ensure_result_size(pr, strlen(newstr) + 1);
+ strcpy(pr->result, newstr);
+}
+void free_prompts(prompts_t *p)
+{
+ size_t i;
+ for (i=0; i < p->n_prompts; i++) {
+ prompt_t *pr = p->prompts[i];
+ smemclr(pr->result, pr->resultsize); /* burn the evidence */
+ sfree(pr->result);
+ sfree(pr->prompt);
+ sfree(pr);
+ }
+ sfree(p->prompts);
+ sfree(p->name);
+ sfree(p->instruction);
+ sfree(p);
+}
+