+ char *out, *ret;
+
+ if (!in || !*in)
+ in = "Default Settings";
+
+ ret = out = snewn(3*strlen(in)+1, char);
+
+ while (*in) {
+ /*
+ * There are remarkably few punctuation characters that
+ * aren't shell-special in some way or likely to be used as
+ * separators in some file format or another! Hence we use
+ * opt-in for safe characters rather than opt-out for
+ * specific unsafe ones...
+ */
+ if (*in!='+' && *in!='-' && *in!='.' && *in!='@' && *in!='_' &&
+ !(*in >= '0' && *in <= '9') &&
+ !(*in >= 'A' && *in <= 'Z') &&
+ !(*in >= 'a' && *in <= 'z')) {
+ *out++ = '%';
+ *out++ = hex[((unsigned char) *in) >> 4];
+ *out++ = hex[((unsigned char) *in) & 15];
+ } else
+ *out++ = *in;
+ in++;
+ }
+ *out = '\0';
+ return ret;
+}
+
+static char *unmungestr(const char *in)
+{
+ char *out, *ret;
+ out = ret = snewn(strlen(in)+1, char);
+ while (*in) {
+ if (*in == '%' && in[1] && in[2]) {
+ int i, j;
+
+ i = in[1] - '0';
+ i -= (i > 9 ? 7 : 0);
+ j = in[2] - '0';
+ j -= (j > 9 ? 7 : 0);
+
+ *out++ = (i << 4) + j;
+ in += 3;
+ } else {
+ *out++ = *in++;
+ }
+ }
+ *out = '\0';
+ return ret;
+}
+
+static char *make_filename(int index, const char *subname)
+{
+ char *env, *tmp, *ret;
+
+ /*
+ * Allow override of the PuTTY configuration location, and of
+ * specific subparts of it, by means of environment variables.
+ */
+ if (index == INDEX_DIR) {
+ struct passwd *pwd;
+
+ env = getenv("PUTTYDIR");
+ if (env)
+ return dupstr(env);
+ env = getenv("HOME");
+ if (env)
+ return dupprintf("%s/.putty", env);
+ pwd = getpwuid(getuid());
+ if (pwd && pwd->pw_dir)
+ return dupprintf("%s/.putty", pwd->pw_dir);
+ return dupstr("/.putty");
+ }
+ if (index == INDEX_SESSIONDIR) {
+ env = getenv("PUTTYSESSIONS");
+ if (env)
+ return dupstr(env);
+ tmp = make_filename(INDEX_DIR, NULL);
+ ret = dupprintf("%s/sessions", tmp);
+ sfree(tmp);
+ return ret;
+ }
+ if (index == INDEX_SESSION) {
+ char *munged = mungestr(subname);
+ tmp = make_filename(INDEX_SESSIONDIR, NULL);
+ ret = dupprintf("%s/%s", tmp, munged);
+ sfree(tmp);
+ sfree(munged);
+ return ret;
+ }
+ if (index == INDEX_HOSTKEYS) {
+ env = getenv("PUTTYSSHHOSTKEYS");
+ if (env)
+ return dupstr(env);
+ tmp = make_filename(INDEX_DIR, NULL);
+ ret = dupprintf("%s/sshhostkeys", tmp);
+ sfree(tmp);
+ return ret;
+ }
+ if (index == INDEX_HOSTKEYS_TMP) {
+ tmp = make_filename(INDEX_HOSTKEYS, NULL);
+ ret = dupprintf("%s.tmp", tmp);
+ sfree(tmp);
+ return ret;
+ }
+ if (index == INDEX_RANDSEED) {
+ env = getenv("PUTTYRANDOMSEED");
+ if (env)
+ return dupstr(env);
+ tmp = make_filename(INDEX_DIR, NULL);
+ ret = dupprintf("%s/randomseed", tmp);
+ sfree(tmp);
+ return ret;
+ }
+ tmp = make_filename(INDEX_DIR, NULL);
+ ret = dupprintf("%s/ERROR", tmp);
+ sfree(tmp);
+ return ret;
+}
+
+void *open_settings_w(const char *sessionname, char **errmsg)
+{
+ char *filename;
+ FILE *fp;
+
+ *errmsg = NULL;
+
+ /*
+ * Start by making sure the .putty directory and its sessions
+ * subdir actually exist. Ignore error returns from mkdir since
+ * they're perfectly likely to be `already exists', and any
+ * other error will trip us up later on so there's no real need
+ * to catch it now.
+ */
+ filename = make_filename(INDEX_SESSIONDIR, NULL);
+ if (mkdir(filename, 0700) != 0) {
+ char *filename2 = make_filename(INDEX_DIR, NULL);
+ mkdir(filename2, 0700);
+ sfree(filename2);
+ mkdir(filename, 0700);
+ }
+ sfree(filename);
+
+ filename = make_filename(INDEX_SESSION, sessionname);
+ fp = fopen(filename, "w");
+ if (!fp) {
+ *errmsg = dupprintf("Unable to create %s: %s",
+ filename, strerror(errno));
+ sfree(filename);
+ return NULL; /* can't open */
+ }
+ sfree(filename);
+ return fp;