+static char *stdin_get_contents(size_t *len_ret)
+{
+ char *buf = NULL;
+ size_t newlen = 1024;
+ size_t pos = 0;
+ size_t inc = 1024;
+ for (;;) {
+ char *p = realloc(buf, newlen);
+ size_t bytes_read, bytes_to_read;
+ if (!p) {
+ imagex_error("out of memory while reading stdin");
+ break;
+ }
+ buf = p;
+ bytes_to_read = newlen - pos;
+ bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
+ pos += bytes_read;
+ if (bytes_read != bytes_to_read) {
+ if (feof(stdin)) {
+ *len_ret = pos;
+ return buf;
+ } else {
+ imagex_error_with_errno("error reading stdin");
+ break;
+ }
+ }
+ newlen += inc;
+ inc *= 3;
+ inc /= 2;
+ }
+ free(buf);
+ return NULL;
+}
+
+enum {
+ PARSE_FILENAME_SUCCESS = 0,
+ PARSE_FILENAME_FAILURE = 1,
+ PARSE_FILENAME_NONE = 2,
+};
+
+static int parse_filename(char **fn_ret, char **line_p, size_t *len_p)
+{
+ size_t len = *len_p;
+ char *line = *line_p;
+ char *fn;
+ int ret;
+ char quote_char;
+
+ /* Skip leading whitespace */
+ for (;;) {
+ if (len == 0)
+ return PARSE_FILENAME_NONE;
+ if (!isspace(*line) && *line != '\0')
+ break;
+ line++;
+ len--;
+ }
+ quote_char = *line;
+ if (quote_char == '"' || quote_char == '\'') {
+ /* Quoted filename */
+ line++;
+ len--;
+ fn = line;
+ line = memchr(line, quote_char, len);
+ if (line) {
+ *line = '\0';
+ len -= line - fn;
+ ret = PARSE_FILENAME_SUCCESS;
+ } else {
+ imagex_error("Missing closing quote: %s", fn - 1);
+ return PARSE_FILENAME_FAILURE;
+ }
+ } else {
+ /* Unquoted filename. Go until whitespace. Line is terminated
+ * by '\0', so no need to check 'len'. */
+ fn = line;
+ do {
+ line++;
+ len--;
+ } while (!isspace(*line) && *line != '\0');
+ *line = '\0';
+ ret = PARSE_FILENAME_SUCCESS;
+ }
+ *len_p = len;
+ *line_p = line;
+ *fn_ret = fn;
+ return ret;
+}
+
+static bool
+parse_source_list_line(char *line, size_t len,
+ struct wimlib_capture_source *source)
+{
+ int ret;
+ ret = parse_filename(&source->fs_source_path, &line, &len);
+ if (ret != PARSE_FILENAME_SUCCESS)
+ return false;
+ ret = parse_filename(&source->wim_target_path, &line, &len);
+ if (ret == PARSE_FILENAME_NONE)
+ source->wim_target_path = source->fs_source_path;
+ return ret != PARSE_FILENAME_FAILURE;
+}
+
+static bool is_comment_line(const char *line, size_t len)
+{
+ for (;;) {
+ if (*line == '#')
+ return true;
+ if (!isspace(*line) && *line != '\0')
+ return false;
+ ++line;
+ --len;
+ if (len == 0)
+ return true;
+ }
+}
+
+static struct wimlib_capture_source *
+parse_source_list(char *source_list_contents, size_t source_list_nbytes,
+ size_t *nsources_ret)
+{
+ size_t nlines;
+ char *p;
+ struct wimlib_capture_source *sources;
+ size_t i, j;
+
+ nlines = 0;
+ for (i = 0; i < source_list_nbytes; i++)
+ if (source_list_contents[i] == '\n')
+ nlines++;
+ sources = calloc(nlines, sizeof(*sources));
+ if (!sources) {
+ imagex_error("out of memory");
+ return NULL;
+ }
+ p = source_list_contents;
+ j = 0;
+ for (i = 0; i < nlines; i++) {
+ char *endp = strchr(p, '\n');
+ size_t len = endp - p + 1;
+ *endp = '\0';
+ if (!is_comment_line(p, len)) {
+ if (!parse_source_list_line(p, len, &sources[j++])) {
+ free(sources);
+ return NULL;
+ }
+ }
+ p = endp + 1;
+
+ }
+ *nsources_ret = j;
+ return sources;
+}
+