+/*
+ * Expands tildes in the file name. Returns data allocated by xmalloc.
+ * Warning: this calls getpw*.
+ */
+char *
+tilde_expand_filename(const char *filename, uid_t uid)
+{
+ const char *path;
+ char user[128], ret[MAXPATHLEN];
+ struct passwd *pw;
+ u_int len, slash;
+
+ if (*filename != '~')
+ return (xstrdup(filename));
+ filename++;
+
+ path = strchr(filename, '/');
+ if (path != NULL && path > filename) { /* ~user/path */
+ slash = path - filename;
+ if (slash > sizeof(user) - 1)
+ fatal("tilde_expand_filename: ~username too long");
+ memcpy(user, filename, slash);
+ user[slash] = '\0';
+ if ((pw = getpwnam(user)) == NULL)
+ fatal("tilde_expand_filename: No such user %s", user);
+ } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */
+ fatal("tilde_expand_filename: No such uid %ld", (long)uid);
+
+ if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
+ fatal("tilde_expand_filename: Path too long");
+
+ /* Make sure directory has a trailing '/' */
+ len = strlen(pw->pw_dir);
+ if ((len == 0 || pw->pw_dir[len - 1] != '/') &&
+ strlcat(ret, "/", sizeof(ret)) >= sizeof(ret))
+ fatal("tilde_expand_filename: Path too long");
+
+ /* Skip leading '/' from specified path */
+ if (path != NULL)
+ filename = path + 1;
+ if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret))
+ fatal("tilde_expand_filename: Path too long");
+
+ return (xstrdup(ret));
+}
+
+/*
+ * Expand a string with a set of %[char] escapes. A number of escapes may be
+ * specified as (char *escape_chars, char *replacement) pairs. The list must
+ * be terminated by a NULL escape_char. Returns replaced string in memory
+ * allocated by xmalloc.
+ */
+char *
+percent_expand(const char *string, ...)
+{
+#define EXPAND_MAX_KEYS 16
+ u_int num_keys, i, j;
+ struct {
+ const char *key;
+ const char *repl;
+ } keys[EXPAND_MAX_KEYS];
+ char buf[4096];
+ va_list ap;
+
+ /* Gather keys */
+ va_start(ap, string);
+ for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
+ keys[num_keys].key = va_arg(ap, char *);
+ if (keys[num_keys].key == NULL)
+ break;
+ keys[num_keys].repl = va_arg(ap, char *);
+ if (keys[num_keys].repl == NULL)
+ fatal("%s: NULL replacement", __func__);
+ }
+ if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL)
+ fatal("%s: too many keys", __func__);
+ va_end(ap);
+
+ /* Expand string */
+ *buf = '\0';
+ for (i = 0; *string != '\0'; string++) {
+ if (*string != '%') {
+ append:
+ buf[i++] = *string;
+ if (i >= sizeof(buf))
+ fatal("%s: string too long", __func__);
+ buf[i] = '\0';
+ continue;
+ }
+ string++;
+ /* %% case */
+ if (*string == '%')
+ goto append;
+ for (j = 0; j < num_keys; j++) {
+ if (strchr(keys[j].key, *string) != NULL) {
+ i = strlcat(buf, keys[j].repl, sizeof(buf));
+ if (i >= sizeof(buf))
+ fatal("%s: string too long", __func__);
+ break;
+ }
+ }
+ if (j >= num_keys)
+ fatal("%s: unknown key %%%c", __func__, *string);
+ }
+ return (xstrdup(buf));
+#undef EXPAND_MAX_KEYS
+}
+
+/*
+ * Read an entire line from a public key file into a static buffer, discarding
+ * lines that exceed the buffer size. Returns 0 on success, -1 on failure.
+ */
+int
+read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
+ u_long *lineno)
+{
+ while (fgets(buf, bufsz, f) != NULL) {
+ if (buf[0] == '\0')
+ continue;
+ (*lineno)++;
+ if (buf[strlen(buf) - 1] == '\n' || feof(f)) {
+ return 0;
+ } else {
+ debug("%s: %s line %lu exceeds size limit", __func__,
+ filename, *lineno);
+ /* discard remainder of line */
+ while (fgetc(f) != '\n' && !feof(f))
+ ; /* nothing */
+ }