* function.c (func_file): Support reading from files.

* NEWS: Add information about reading files.
* make.texi (File Function): Describe reading files.
* tests/scripts/functions/file: Test new features for $(file ...)
diff --git a/NEWS b/NEWS
index 3a62d69..a2db010 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,10 @@
   successful or not "0" if not successful.  The variable value is unset if no
   != or $(shell ...) function has been invoked.
 
+* The $(file ...) function can now read from a file with $(file <FILE).
+  The function is expanded to the contents of the file.  The contents are
+  expanded verbatim except that the final newline, if any, is stripped.
+
 * VMS-specific changes:
 
   * Perl test harness now works.
diff --git a/doc/make.texi b/doc/make.texi
index afce1c5..33b1473 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -7521,13 +7521,22 @@
 @findex file
 @cindex writing to a file
 @cindex file, writing to
+@cindex reading from a file
+@cindex file, reading from
 
-The @code{file} function allows the makefile to write to a file.  Two
-modes of writing are supported: overwrite, where the text is written
-to the beginning of the file and any existing content is lost, and
-append, where the text is written to the end of the file, preserving
-the existing content.  In all cases the file is created if it does not
-exist.
+The @code{file} function allows the makefile to write to or read from
+a file.  Two modes of writing are supported: overwrite, where the text
+is written to the beginning of the file and any existing content is
+lost, and append, where the text is written to the end of the file,
+preserving the existing content.  In both cases the file is created if
+it does not exist.  It is a fatal error if the file cannot be opened
+for writing, or if the write operation fails.  The @code{file}
+function expands to the empty string when writing to a file.
+
+When reading from a file, the @code{file} function expands to the
+verbatim contents of the file, except that the final newline (if there
+is one) will be stripped.  Attempting to read from a non-existent file
+expands to the empty string.
 
 The syntax of the @code{file} function is:
 
@@ -7535,21 +7544,23 @@
 $(file @var{op} @var{filename}[,@var{text}])
 @end example
 
-The operator @var{op} can be either @code{>} which indicates overwrite
-mode, or @code{>>} which indicates append mode.  The @var{filename}
-indicates the file to be written to.  There may optionally be
+When the @code{file} function is evaluated all its arguments are
+expanded first, then the file indicated by @var{filename} will be
+opened in the mode described by @var{op}.
+
+The operator @var{op} can be @code{>} to indicate the file will be
+overwritten with new content, @code{>>} to indicate the current
+contents of the file will be appended to, or @code{<} to indicate the
+contents of the file will be read in.  The @var{filename} specifies
+the file to be written to or read from.  There may optionally be
 whitespace between the operator and the file name.
 
-When the @code{file} function is expanded all its arguments are
-expanded first, then the file indicated by @var{filename} will be
-opened in the mode described by @var{op}.  Finally @var{text} will be
-written to the file.  If @var{text} does not already end in a newline,
-even if empty, a final newline will be written.  If the @var{text}
-argument is not given, nothing will be written.  The result of
-evaluating the @code{file} function is always the empty string.
+When reading files, it is an error to provide a @var{text} value.
 
-It is a fatal error if the file cannot be opened for writing, or if
-the write operation fails.
+When writing files, @var{text} will be written to the file.  If
+@var{text} does not already end in a newline a final newline will be
+written (even if @var{text} is the empty string).  If the @var{text}
+argument is not given at all, nothing will be written.
 
 For example, the @code{file} function can be useful if your build
 system has a limited command line size and your recipe runs a command
diff --git a/function.c b/function.c
index 9f9f0fc..a80f194 100644
--- a/function.c
+++ b/function.c
@@ -2208,27 +2208,67 @@
         }
       fn = next_token (fn);
 
-      fp = fopen (fn, mode);
+      if (fn[0] == '\0')
+        O (fatal, *expanding_var, _("file: missing filename"));
+
+      ENULLLOOP (fp, fopen (fn, mode));
       if (fp == NULL)
-        {
-          const char *err = strerror (errno);
-          OSS (fatal, reading_file, _("open: %s: %s"), fn, err);
-        }
+        OSS (fatal, reading_file, _("open: %s: %s"), fn, strerror (errno));
+
       if (argv[1])
         {
           int l = strlen (argv[1]);
           int nl = l == 0 || argv[1][l-1] != '\n';
 
           if (fputs (argv[1], fp) == EOF || (nl && fputc ('\n', fp) == EOF))
-            {
-              const char *err = strerror (errno);
-              OSS (fatal, reading_file, _("write: %s: %s"), fn, err);
-            }
+            OSS (fatal, reading_file, _("write: %s: %s"), fn, strerror (errno));
         }
-      fclose (fp);
+      if (fclose (fp))
+        OSS (fatal, reading_file, _("close: %s: %s"), fn, strerror (errno));
+    }
+  else if (fn[0] == '<')
+    {
+      char *preo = o;
+      FILE *fp;
+
+      fn = next_token (++fn);
+      if (fn[0] == '\0')
+        O (fatal, *expanding_var, _("file: missing filename"));
+
+      if (argv[1])
+        O (fatal, *expanding_var, _("file: too many arguments"));
+
+      ENULLLOOP (fp, fopen (fn, "r"));
+      if (fp == NULL)
+        {
+          if (errno == ENOENT)
+            return o;
+          OSS (fatal, reading_file, _("open: %s: %s"), fn, strerror (errno));
+        }
+
+      while (1)
+        {
+          char buf[1024];
+          size_t l = fread (buf, 1, sizeof (buf), fp);
+          if (l > 0)
+            o = variable_buffer_output (o, buf, l);
+
+          if (ferror (fp))
+            if (errno != EINTR)
+              OSS (fatal, reading_file, _("read: %s: %s"), fn, strerror (errno));
+          if (feof (fp))
+            break;
+        }
+      if (fclose (fp))
+        OSS (fatal, reading_file, _("close: %s: %s"), fn, strerror (errno));
+
+      /* Remove trailing newline.  */
+      if (o > preo && o[-1] == '\n')
+        if (--o > preo && o[-1] == '\r')
+          --o;
     }
   else
-    OS (fatal, reading_file, _("Invalid file operation: %s"), fn);
+    OS (fatal, *expanding_var, _("file: invalid file operation: %s"), fn);
 
   return o;
 }
diff --git a/tests/scripts/functions/file b/tests/scripts/functions/file
index 55eb58a..904db79 100644
--- a/tests/scripts/functions/file
+++ b/tests/scripts/functions/file
@@ -115,4 +115,47 @@
 
 unlink('file.out');
 
+# Reading files
+run_make_test(q!
+$(file >file.out,A = foo)
+X1 := $(file <file.out)
+$(file >>file.out,B = bar)
+$(eval $(file <file.out))
+
+x:;@echo '$(X1)'; echo '$(A)'; echo '$(B)'
+!,
+              '', "A = foo\nfoo\nbar\n");
+
+unlink('file.out');
+
+# Reading from non-existent file
+run_make_test(q!
+X1 := $(file <file.out)
+x:;@echo '$(X1)';
+!,
+              '', "\n");
+
+# Extra arguments in read mode
+run_make_test(q!
+X1 := $(file <file.out,foo)
+x:;@echo '$(X1)';
+!,
+              '', "#MAKEFILE#:2: *** file: too many arguments.  Stop.\n", 512);
+
+
+# Missing filename
+run_make_test('$(file >)', '',
+              "#MAKEFILE#:1: *** file: missing filename.  Stop.\n", 512);
+
+run_make_test('$(file >>)', '',
+              "#MAKEFILE#:1: *** file: missing filename.  Stop.\n", 512);
+
+run_make_test('$(file <)', '',
+              "#MAKEFILE#:1: *** file: missing filename.  Stop.\n", 512);
+
+# Bad call
+
+run_make_test('$(file foo)', '',
+              "#MAKEFILE#:1: *** file: invalid file operation: foo.  Stop.\n", 512);
+
 1;