+/* Read standard input until EOF and return the full contents in a malloc()ed
+ * buffer and the number of bytes of data in @len_ret. Returns NULL on read
+ * error. */
+static char *
+stdin_get_contents(size_t *len_ret)
+{
+ /* stdin can, of course, be a pipe or other non-seekable file, so the
+ * total length of the data cannot be pre-determined */
+ 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(T("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(T("error reading stdin"));
+ break;
+ }
+ }
+ newlen += inc;
+ inc *= 3;
+ inc /= 2;
+ }
+ free(buf);
+ return NULL;
+}
+
+
+static tchar *
+translate_text_to_tstr(char *text, size_t num_bytes,
+ size_t *num_tchars_ret)
+{
+#ifndef __WIN32__
+ /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
+ * */
+ *num_tchars_ret = num_bytes;
+ return text;
+#else /* !__WIN32__ */
+ /* On Windows, translate the text to UTF-16LE */
+ wchar_t *text_wstr;
+ size_t num_wchars;
+
+ if (num_bytes >= 2 &&
+ ((text[0] == 0xff && text[1] == 0xfe) ||
+ (text[0] <= 0x7f && text[1] == 0x00)))
+ {
+ /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
+ * with something that looks like an ASCII character encoded as
+ * a UTF-16LE code unit. Assume the file is encoded as
+ * UTF-16LE. This is not a 100% reliable check. */
+ num_wchars = num_bytes / 2;
+ text_wstr = (wchar_t*)text;
+ } else {
+ /* File does not look like UTF-16LE. Assume it is encoded in
+ * the current Windows code page. I think these are always
+ * ASCII-compatible, so any so-called "plain-text" (ASCII) files
+ * should work as expected. */
+ text_wstr = win32_mbs_to_wcs(text,
+ num_bytes,
+ &num_wchars);
+ free(text);
+ }
+ *num_tchars_ret = num_wchars;
+ return text_wstr;
+#endif /* __WIN32__ */
+}
+
+static tchar *
+file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
+{
+ char *contents;
+ size_t num_bytes;
+
+ contents = file_get_contents(filename, &num_bytes);
+ if (!contents)
+ return NULL;
+ return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
+}
+
+static tchar *
+stdin_get_text_contents(size_t *num_tchars_ret)
+{
+ char *contents;
+ size_t num_bytes;
+
+ contents = stdin_get_contents(&num_bytes);
+ if (!contents)
+ return NULL;
+ return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
+}
+